Java RMI and Class Download

Readme/index.html file

Due to "strange" and unexplained (at least for me) policies/changes  of this server, this page/site is out of date since Jun 2009. The new page/site with this content is

 

Copyright (C) 2007 Fernando G. Tinetti

1.- Introduction

What follows is a short review of class download in the context of Java RMI.
Conceptually, there are not "client" and "server" processes, but in this
description it will be considered that the process or thread that makes the
invocation to a remote method will be the "client" and the process or thread
that implements and runs the invoked method will be the "server".
Furthermore, this approach matches almost every RMI explanation in the
literature. Different alternatives will be described from "no download at
all" to "download on client and server side". Every alternative will be
directly related to RMI, not necessarilly all the application possibilities
are going to be examined. Every example has been tested under Linux Kernel
2.6, more specifically

$ uname -sr
Linux 2.6.15-1.2054_FC5

and with java jdk-1_5_0_06-linux-i586. More specifically,

$ javac -version
javac 1.5.0_06
...
$ java -version
java version "1.5.0_06"
...

Different configurations/designs for class download:

1) No download, "basic" Java RMI. Every class needed is on the server
and client side before startup.

2) Server side download, class of method parameter downloaded. This is the
minimum server side meaningful download, since the method parameter could
be unknown on startup time provided that the parameter has a known interface
at the server side.

3) Client side download, class of method return downloaded. This is the
minimum client side meaningful download, since the method return could be
unknown on startup time provided that the method return class has a known
interface at the client side.



2.- The "basic" Java RMI: no download of classes defined
for/related to the application



This alternative/configuration is included here just as an introduction
to Java RMI as well as to introduce class download in the context of
Java RMI. Also, the example used on every class download explanation/
alternative configuration will be given here. Some technical concepts
are not completely justified and/or with non complete reasons to exist,
but it is not worse than the Java RMI explanations given by Sun.

In fact, since Java version 1.5.xxx the server stub (in terms of Java
RMI) is downloaded in the client side. This can be considered the
minimum class download, since it's the server stub on the client side.
Server stub download should be fully transparent
(but it's not), because it's nothing to do with server or client
functionality, it seems to be just an implementation requirement
(for Java RMI). However, there are some lines of Java code that should
appear to accomplish this task at the server side. On Java versions
previous to 1.5.xxx the server stub had to be explicitly generated with
rmic [7] and, also, it had to be explicitly downloaded if
required/decided such a functionality for the application.

From a high level point of view, Java RMI provides a facility to program
a distributed Java based application, as explained in the Java RMI
website [1]. More specifically, Java RMI provides a facility to make a
method invocation on an object which is not on the local JVM. From this
point of view, a "remote invocation" made by a client is, in fact, a
reference to a remote object. As such, on the client side it has to be
known "something" on the remote object. This is why the server interface
has to be defined. As the first and most simple example, consider the
following interface, in the file RemoteIface.java

/*
 * RemoteIface.java
 */

import java.rmi.*;    /* Remote class and RemoteException exception */

public interface RemoteIface extends Remote
{
  /* The method to be invoked from other JVMs */
  public byte[] sendThisBack(byte[] data) throws RemoteException;
}

Note that it's not enough to have a server interface, the server
interface has to be "Remote Method Invocation enabled". This implies:
  a) The interface should extend Remote.
  b) The method/s should throw the exception RemoteException.
And both decisions/definitions seems to be naturally justified, since
this interface has been defined in order to have a way on the client
side to invoke/use a remote object. There may be as many "remote
methods" as needed, and this is why it's important to remember that
the object, not the method is remote (even when Java named RMI: Remote
Method Invocation instead of ROR: Remote Object Reference, but this
has other implications beyond the scope of this discussion...).

Given an interface for a server, the implementation should be no
more than implementing the methods defined as "RMI enabled". In this
document, the interface implementation will be given independently of
the application which creates and makes this object available to
other application/s. This decision makes easier to define/use/explain
classes "RMI enabled" and, also, allows to explain different concepts
on different places. The class implementing the interface RemoteIface
given above would be in the file RemoteClass.java

/*
 * RemoteClass.java
 */

import java.rmi.*;    /* RemoteException exception */

public class RemoteClass implements RemoteIface
{
  /* Constructor */
  public RemoteClass() {}

  /* The method to be invoked from other JVMs */
  public byte[] sendThisBack(byte[] data) throws RemoteException
  {
    System.out.println("Data back to client");
    return data;
  }
}

Note that except for the line

importjava.rmi.*;

and the exception RemoteException, the implementation class
given to the interface does not difer from any implementation
class given for an interface. The task for a distributed
application programmer is, thus, simplified.

As already noted, it's necessary an application which creates and
makes available an object of this class to other applications
(applications running on other JVMs). An object of this class is
created as any other object, e.g.

  RemoteClass robject = new RemoteClass();

but making this object available to other applications is specifically
related to Java RMI and, also to the Java RMI "evolution" (just to name
it in some way). In terms of Java RMI, it is necessary to [2]
  a) Export the remote object to the Java RMI runtime.
  b) Register the remote object, making an association between a name
     (string) and an object's stub.
And both tasks are directly related to Java RMI behavior. More
specifically, an object can be created and exported to the Java RMI
runtime with

  RemoteClass robject = new RemoteClass();
  RemoteIface riface  = (RemoteIface) UnicastRemoteObject.exportObject(robject, 0);

where UnicastRemoteObject.exportObject makes, in fact, two important
tasks [2]:
  a) Makes the object ready to receive incoming invocations.
  b) Returns an object's stub which is necessary on the client side.
It is necessary now to provide some way to the clients for obtaining
the stub, which is known as "registering" the object under a name.
Registration will be made in this example by using the method rebind
provided by the class java.rmi.Naming. Now, an object can be created,
exported, and registered with

  RemoteClass robject = new RemoteClass();
  RemoteIface riface  = (RemoteIface) UnicastRemoteObject.exportObject(robject, 0);
  Naming.rebind("remote", riface);

i.e. the object is running and listening to incoming invocations and
registered under the name "remote". Method rebind of class
java.rmi.Naming requires a registry (in terms of Java RMI). The
registry is provided by the rmiregistry command (external to the JVM).
The rmiregistry process has to be running on the host where the
server is running. This process is, in fact, the one that efectively
registers the object, i.e. associates a name (string) to an object
and provides the corresponding stub to the clients. A file
StartRemoteObject.java can define a class with only a main method with
the tasks explained above

/*
 * StartRemoteObject.java
 */

import java.rmi.server.UnicastRemoteObject;  /* exportObject */
import java.rmi.Naming;                      /* rebind       */


public class StartRemoteObject
{
  public static void main (String args[])
  {
    try{
      /* Create ("start") the object which has the remote method */
      RemoteClass robject = new RemoteClass();

      /* Obtain something to export to te client side... the "stub"... */
      RemoteIface riface  = (RemoteIface) UnicastRemoteObject.exportObject(robject, 0);

      /* Register the object on the rmiregistry */
      Naming.rebind("remote", riface);
    } catch (Exception e)
    {
        System.out.println("Hey, something is missing");
        e.printStackTrace();
        System.out.println(e.getMessage());
    }
  }
}

There is nothing else to program on the server side. Furthermore,
the server is able to be running at this point, with a little number
of steps/commands:
  a) Compile:                                        $> javac *.java
  b) Run rmiregistry:                             $> rmiregistry &
  c) Run Java with StartRemoteObject:   $> java StartRemoteObject 
And, after these commands, the server is running and waiting to be
invoked from other JVMs. The server process is running until
something like ^C, and the rmiregistry could be killed with
"killall rmiregistry". Both tasks are recommended in order to
restart everything again, once the client process is ready to
run.

The server is complete, but some important concepts/statements
deserve some explanations:
  a) A little bit of history: The mechanism of registering using
     another process (independent from client and server,
     rmiregistry in Java RMI) could be rather strange for all
     those programmers that did not use/know RPC (Remote
     Procedure Call). For example: why not "publish" the
     object/method directly on a socket? Well, despite the
     possible choices, RPC (also defined by Sun, but some
     years earlier than Java) works just in this way: those
     procedures that can be remotely called have to be registered
     on the computer where they are implemented, by another
     process: the portmapper. It is not necessarilly a bad idea,
     it seems to be an idea that solved the problem for RPC
     and is just reused for RMI. In fact, Sun recognizes in
     [3] that Java RMI is just the way in which RPC is
     defined/used for Java.

  b) The server stub download on the client side is not completely
     hidden on the server side. More specifically, the line

  RemoteIface riface = (RemoteIface) UnicastRemoteObject.exportObject(robject, 0);

     is strictly necessary, since it's where the server stub is
     generated. The original Java RMI (and now non existent,
     since Java 1.5.xxx) stub-skeleton [6] discussion will be
     avoided here, just to avoid some inconsistencies and, also,
     some wasting of time. However, it's clear that it is
     "natural" to think that having defined the class as extending
     Remote, exporting as well as handling the stub download
     should be handled automatically (i.e. hidden to the
     programmer). As an advance in Java 1.5.xxx, the skeleton
     is not necessary anymore as well as the static stub
     generation, which had to be made with rmic (rmi compiler)
     instead of being generated at run time. From the point of
     view of hiding details on the server side, RPC could be
     considered better: at least using rpcgen on Linux only
     the server code has to be programmed, i.e. the code for
     services/procedures to be remotely called. This behavior
     corresponds to the interface implementation, i.e. the
     RemoteClass class given in this document. The code of
     StartRemoteObject class in the example given in this
     document is automatically generated with rpcgen for RPC
     on Linux.

  c) In [2], the object is registered with the method bind of
     class Registry. In the example given in this page, the
     Naming class is used and this class solves everything
     related to the rmiregistry. Using the method bind of class
     Registry requires to get a reference to the rmiregistry on
     the application, which is made in [2] by using the method
     getRegistry of LocateRegistry.

  d) As a very specific detail, the rmiregistry should have
     access to the compiled interface, RemoteIface.class in this
     example. A priori, this requirement seems to be very
     restrictive for very large distributed applications, but it's
     not a problem for the little example given in this document,
     just start the rmiregistry on the directory contaning the
     server and, specifically, the compiled interface.

The server side is complete and running, the client should be
programmed and the complete client/server example could be used.
There should be some way to make an invocation on the remote object
(i.e. the object not in the local JVM) on the client side.
Furthermore, RMI has been defined to make invocations on remote
objects as similar as possible to an invocation on a local object
(i.e. an object on the JVM where the process/thread makes the
invocation). More specifically, on the client, there should be code
like

  ...
  RemoteIface remoteobj;
  ...
  buffer = new byte[bufferlength];
  remoteobj.sendThisBack(buffer);
  ...

i.e. an object which is an implementation of the remoteIface
interface (remote in this example) and an invocation on that object.
Note that at this point the invocation is exactly the same as if the
object remote would be local. As it might be expected, the remote
object remoteobj is not obtained with a simple "new" on some class:
it's necessary to obtain a reference to an object in another JVM,
the server stub. This task will be made in this document by using
an URL and the rmiregistry. Basically, the server object (the one
used "remotely") has been registered/accounted on a process running
on the same computer as the JVM that contains it, i.e. has been
registered with the rmiregistry. The rmiregistry has to be running
on the same computer where the JVM containing the remote object is
running (the server side). Thus, the client has to "lookup" (in Java
RMI terms) for the object and this may be made by using an URL.
Treated as an URL, the client needs to know the "Location" of the
rmiregistry and, thus, the remote object itself. The rmiregistry uses
a well known port (a TCP or UDP port) to register and lookup remote
objects. Usually, it's the 1099, but it may be changed on rmiregistry
startup. Assuming that the object has been successfully registered
(with rmiregistry) on the server under the name "remote", the URL to
use for remote object lookup could be

  //<hostname>:1099/remote

where <hostname> should be replaced by the DNS domain name that could
be solved to the server host on the client host. The way in which the
client could obtain a reference to the object can be added to the
previous example

  ...
  import java.rmi.Naming;                    /* lookup(...) */
  ...
  RemoteIface remoteobj;
  remoteobj = (remoteIface) Naming.lookup("//localhost:1099/remote");
  ...
  buffer = new byte[bufferlength];
  remoteobj.sendThisBack(buffer);
  ...

assuming that the server is running on the same computer, localhost.
Class java.rmi.Naming solves (through the method lookup) the problem
of binding a URL to a remote object already created and registered on
the rmiregistry process under that URL. Basically, the method lookup
asks to the rmiregistry running on the host and listening on the port
indicated by the URL and returns a reference (the stub, in fact) to
the object if the rmiregistry successfully returns such a reference
to the object. The client can be completely defined right now on a
file AskRemote.java

/*
 * AskRemote.java
 */

import java.rmi.Naming;                    /* lookup         */
import java.rmi.registry.Registry;         /* REGISTRY_PORT  */

import java.net.MalformedURLException;     /* Exceptions...  */
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class AskRemote
{
  public static void main(String[] args)
  {
    /* Look for hostname and msg length in the command line */
    if (args.length != 1)
    {
      System.out.println("1 argument needed: (remote) hostname");
      System.exit(1);
    }

    try {
      String rname = "//" + args[0] + ":" + Registry.REGISTRY_PORT + "/remote";

      RemoteIface remoteobj;
      remoteobj = (RemoteIface) Naming.lookup(rname);

      byte buffer[];
      int  bufferlength = 100;

      buffer = new byte[bufferlength];

      remoteobj.sendThisBack(buffer);

      System.out.println("Done");
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (RemoteException e) {
        e.printStackTrace();
    } catch (NotBoundException e) {
        e.printStackTrace();
    }
  }
}

Where this client version adds two implementation details to the
previous example:
  a) The hostname where the remote object is contained has to be
     given as a command line parameter to the application, args[0].
  b) Instead of using a hardcoded port (1099 in the previous example),
     it's used the value given by
     java.rmi.registry.Registry.REGISTRY_PORT
     to construct the URL for lookup.

This client code does not hide some details compared to RPC
implemented on C under Linux, for example. The Java RMI programmer
necessarilly has to make the URL, the lookup invocation, etc., while
the C programmer (at least using rpcgen on Linux, for example) does
not necessarilly have to engage on these details. Most of the times
(if not always) all of these details are hiden by a "simple" call
to a function like clnt_create(...).

So far, the client side can be considered complete. What about
classes and class download? all that is needed on the client side
seems to be
  a) RemoteIface.class (the compiled remoteIface.java)
  b) AskRemote.class (the compiled askRemote.java)
Well, yes and no. From the programmer point of view: yes. There is
nothing else to program. However, there is one more necessary class
(a .class file, in particular): the server stub, but it comes to the
client as the obect returned by the method lookup().
On the server side, the classes needed are
  a) RemoteIface.class (the compiled RemoteIface.java)
  b) RemoteClass.class (the compiled RemoteClass.java, which is an
     implementation for the interface RemoteIface.java)
  c) StartRemoteObject.class (the compiled StartRemoteObject.java)

Now, the complete client/server application is ready to run. Client
and server Java RMI applications will be on different computers/hosts
most (if not all) of the times. However, different JVMs are, in fact,
different computers for Java applications even when running on the
same computer/host. Also, having everything on a single computer
simplifies debugging and, in this simple example, simplifies the
running environment: just a few consoles/terminals on a computer.
Binary/Java classes distribution becomes important because resembles
the distribution on different computers. Remember to compile every
.java file with

  javac *.java

In this example, the server side, i.e. the binaries needed on the
server side, will be in a directory named server

  mkdir server

and should contain the files RemoteClass.class, RemoteIface.class,
and StartRemoteObject.class, e.g.

  cp RemoteClass.class RemoteIface.class StartRemoteObject.class server/

The client side, i.e. the binaries needed on the client side,
will be in a directory named client

  mkdir client

and and should contain the files RemoteIface.class, and
AskRemote.class, e.g.

  cp RemoteIface.class AskRemote.class client/

The server should be started on it's own console with the sequence

  cd server
  rmiregistry &
  java StartRemoteObject

and this console will not be available for anything else. Remember
that rmiregistry as well as the JVM running StartRemoteObject should
have access to the "server side" classes, those copied in the server
directory. The client should be started on another console with the
sequence

  cd client
  java AskRemote localhost

In this case, on the client console will appear

  About to lookup for //localhost:1099/remote
  Done

And in the server console will appear

  Data back to client

The only difference if running the client on a different
computer should be the command line argument to AskRemote,
the DNS name of the computer where the server should be
given. Furthermore, the server side should be reachable
from the client side, including firewall configuration
and/or rules to/from port 1099.

The basic Java RMI example is complete now, and the server
stub is downloaded at runtime by the "Java RMI environment".
The complete code can be found in nodonload.tar, including
the client and server side directories with the
corresponding .class files and some scripts to run the
applications.



3.- Download at the server side: compute server and
mobile agent examples



The facility for class download on the server side is
very well exemplified in [3]. In fact, the idea is general
enough for allowing some kind of processing on a set
of "servers" reachable from some computer. This last
example is given in [4] as mobile agents, and code example
can be obtained at [5].

3.1.- The first example

In the first (and simple) example, it will be assumed that
defined on a file ObjIface.java as

/*
 * ObjIface.java
 */

import java.io.Serializable;

public interface ObjIface extends Serializable
{
    int justToSee();
}

i.e. a method returning an int value. Note that this
interface (and the class which implements it) has only
one requirement: the method justToSee(). Note, also, that
the interface should extend class Serializable, since the
objects should be able to be trasferred on the network.
The server interface remains extending Remote and could
be defined in the file RemoteIface2.java as

/*
 * RemoteIface2.java
 */

import java.rmi.*;    /* Remote class and RemoteException exception */

public interface RemoteIface2 extends Remote
{
  /* The method to be invoked from other JVMs */
  public int recvAndExec(ObjIface obj) throws RemoteException;
}

It is expected that the server receive an object of some
implementation of ObjIface, invokes the method justToSee()
and returns the int value returned by the method justToSee().
An implementation for the server interface could be on the
file RemoteClass2.java as

/*
 * RemoteClass2.java
 */

import java.rmi.*;    /* RemoteException exception */

public class RemoteClass2 implements RemoteIface2
{
  /* Constructor */
  public RemoteClass2() {}

  /* The method to be invoked from other JVMs */
  public int recvAndExec(ObjIface obj) throws RemoteException
  {
    System.out.println("Invoking the received object method");
    return obj.justToSee();
  }
}

The server interface RemoteIface2 is needed on the client side
in order to make a remote method invocation. The interface
ObjIface is needed on the server side in order to receive an
object without an available local class and, also, make an
invocation on the received object. The implementation of the
interface will be downloaded at runtime in the server side.

The server side should be complete with a class which creates,
exports, and registers an object of class RemoteClass2. In fact,
this is exactly done in the previous StartRemoteObject class,
and it will be used "directly" as the StartRemoteObject2.java,
replacing RemoteClass by RemoteClass2 and RemoteIface by
RemoteIface2

/*
 * StartRemoteObject2.java
 */

import java.rmi.server.UnicastRemoteObject;  /* exportObject */
import java.rmi.Naming;                      /* rebind       */


public class StartRemoteObject2
{
  public static void main (String args[])
  {
    try{
      /* Create ("start") the object which has the remote method */
      RemoteClass2 robject = new RemoteClass2();

      /* Obtain something to export to te client side... the "stub"... */
      RemoteIface2 riface  = (RemoteIface2) UnicastRemoteObject.exportObject(robject, 0);

      /* Register the object on the rmiregistry */
      Naming.rebind("remote", riface);
    } catch (Exception e)
    {
        System.out.println("Hey, something is missing");
        e.printStackTrace();
        System.out.println(e.getMessage());
    }
  }
}

However, it's clear that something has to be done for the class
downloading at the server side. The first technical detail to be
solved at the server side is to create a security manager (in terms
of Java and Java RMI documents like [3]) with some security policy.
This can be made in the StartRemoteObject2 class [3] or, directly,
on the JVM startup. The security policy can be defined in a
java.policy file like

grant {
   permission java.net.SocketPermission "*:1099",
      "connect, accept, resolve";
   permission java.net.SocketPermission "*:1024-65535",
      "connect, accept";
   permission java.net.SocketPermission "*:80",
      "connect";
};

which could be considered permissive, but good enough for this
example. This security policy could be given to the JVM at startup
with the option

  -Djava.security.policy=java.policy

and the security manager could be created at startup with the
option

  -Djava.security.manager

The security manager as well as the security policy "only" allow
dynamic class download to the JVM. The other technical detail at
the server side is to provide the necessary information to look
for classes to be downloaded. More specifically, an URL has to
be given to the Java RMI environment for class download with
the option -Djava.rmi.server.codebase as in

  -Djava.rmi.server.codebase=http://localhost/distrclasses/

where it's assumed that there is a local http server which has
classes binaries under the directory distrclasses. Now, the server
side is complete and ready to run assuming the rmiregistry is
running and some implementatio of interface objIface is reachable
through a local http server.

The client side still remains to be programmed. However, the
client side is similar to the previous one (without dynamic class
download). On the client side, it's expected to find an
implementation for the interface ObjIface, and it could be on
the file ObjClass.java as

/*
 * ObjClass.java
 */

public class ObjClass implements ObjIface
{
  private int priv_id;

  public ObjClass(int id)
  {
    priv_id = id;
  }

  public int justToSee()
  {
    return priv_id;
  }
}

where the constructor is used to give an int value which is later
returned on each justToSee() invocation. The client could be very
similar to AskRemote, but creates and sends as the remote method
invocation parameter an object of class ObjClass. An example of
client (a rather hardcoded one) could be on the file
AskRemote2.java as

/*
 * AskRemote2.java
 */

import java.rmi.Naming;                    /* lookup         */
import java.rmi.registry.Registry;         /* REGISTRY_PORT  */

import java.net.MalformedURLException;     /* Exceptions...  */
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class AskRemote2
{
  public static void main(String[] args)
  {
    /* Look for hostname and msg length in the command line */
    if (args.length != 1)
    {
      System.out.println("1 argument needed: (remote) hostname");
      System.exit(1);
    }

    try {
      String rname = "//" + args[0] + ":" + Registry.REGISTRY_PORT + "/remote";

      RemoteIface2 remoteobj;
      remoteobj = (RemoteIface2) Naming.lookup(rname);

      objClass oneobj = new objClass(1);

      int retval;
      retval = remoteobj.recvAndExec(oneobj);

      System.out.println("Done with return value = " + retval);
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (RemoteException e) {
        e.printStackTrace();
    } catch (NotBoundException e) {
        e.printStackTrace();
    }
  }
}

Client as well as server side code should compile with

  javac ObjIface.java RemoteIface2.java RemoteClass2.java \
        StartRemoteObject2.java ObjClass.java AskRemote2.java

As in the example without class download, the binaries
needed on the server side, will be in a directory named server2

  mkdir server

and should contain the files RemoteClass2.class, RemoteIface2.class,
StartRemoteObject.class, ObjIface.class, and the policy file
java.policy, e.g.

  cp RemoteClass2.class RemoteIface2.class StartRemoteObject2.class \
     ObjIface.class java.policy server/

The binaries needed on the client side will be in a directory
named client

  mkdir client

and should contain the files RemoteIface2.class, AskRemote2.class,
and ObjClass.class, e.g.

  cp RemoteIface2.class AskRemote2.class ObjIface.class ObjClass.class \
     client/

And, also on the client side, the ObjClass.class should be stored
on the directory seved by the http server as

  http://localhost/distrclasses/

The server should be started on it's own console with the sequence

  cd server
  rmiregistry &
  java -Djava.security.policy=java.policy -Djava.security.manager \
       -Djava.rmi.server.codebase=http://localhost/distrclasses/ \
       StartRemoteObject2

and this console will not be available for anything else. Remember
that rmiregistry as well as the JVM running StartRemoteObject2 should
have access to the "server side" classes, those copied in the server
directory. Remember to copy the client side class ObjClass.class in the
proper directory, and the client should be started on another console
with the sequence

  cd client
  java AskRemote2 localhost

In this case, on the client console will appear

  Done with return value = 1

And in the server console will appear

  Invoking the received object method

As another, more general example, the class file ObjClass.class
could be stored at the site

  http://ftinetti.googlepages.com

and the server should be started with

  cd server
  rmiregistry &
  java -Djava.security.policy=java.policy -Djava.security.manager \
       -Djava.rmi.server.codebase=http://ftinetti.googlepages.com/ \
       StartRemoteObject2

Server and client should produce the same output, but the
class ObjClass is downloaded from the site

  http://ftinetti.googlepages.com

The complete code can be found in downloadatserver.tar,
including the client and server side directories with the
corresponding .class files and some scripts to run the
applications. This code can be directly used with the
.class file already stored at a local http server or,
changing the codebase property, with the .class file in
the site ftinetti.googlepages.com (in this last case,
nothing else has to be done).

This first example of class download in the server side
shows useful details:
  a) Every class that extending java.io.Serializable can
     be downloaded at runtime.
  b) Code (methods) in the class to be downloaded can be
     as complex as needed, i.e. it is expected that
     methods complexity do not complicate a "class
     serialization" which allows such a class to be
     downloaded at runtime.
  c) rmiregistry as well as the server side only have to
     know the class interface at startup and the place to
     look for unknown class/es at runtime.

Furthermore, this first simple/ied example can be used to
show how to take advantage of a big host as a compute
server where users can submit their jobs. Instead of
installing binaries statically, an object capable of
executing at the server side may be sent with Java RMI
is. The server now receive as a parameter an object whose
class defines some method that returns, in general,
an object. Again, the server itself will be in charge
of invoking the object's method locally (usually, as the
first and only task).

3.2.- The second example: a compute server

In fact, the first example of class dowload should be
enhanced just for the class received at the server side
to handle other class/es. In particular, the method/s
should be able to return any serializable object. An
object "serializability" is specified as in the
previous ObjIface, i.e. extending the class
Serializable. The second example of dynamic class
download at the server side will have the classes:
  a) TaskClass: the class donwloaded at the server
     side which has one public method: run(). This
     class is similar to ObjClass, but the method run()
     would return any serializable class, on only an
     integer value, which is returned by the justToSee()
     method of class ObjClass.
  b) RetClass: the serializable class returned by method
     run() of class TaskClass. RetClass would be the
     generalization of "int" in the previous example,
     which is returned by method justToSee().
  c)

Thus, two class interfaces are needed at the server side
to be downloaded at runtime

/*
 * TaskIface.java
 */

import java.io.Serializable;

public interface TaskIface extends Serializable
{
  RetClassIface run();
}

/*
 * RetIface.java
 */

import java.io.Serializable;

public interface RetIface extends Serializable
{
  /* Needed methods here, used in the run() implementation */
}

Interface implementations should be defined (and useful) at
the client side and, also available via http o ftp server to
the server side

/*
 * RetClass.java
 */

public class RetClass implements RetIface
{
  /* Other methods here, used in run() implementation */
}

/*
 * TaskClass.java
 */

public class TaskClass implements TaskIface
{
  public RetIface run()
  {
    RetIface priv_obj;

    /* There should be an object to return... */
    priv_obj = new RetClass();

    /* Some code here, depending on the application */
    return priv_obj;
  }
}

At this point, the classes to be downloaded at runtime
in the server side are completely defined (even when those
classes do not seem to make any useful work). The rest of
the server side is as expected for a compute server: the
RMI "classical" code combined with the tasks which return
some serializable object

/*
 * CompServerIface.java
 */

import java.rmi.*;    /* Remote class and RemoteException exception */

public interface CompServerIface extends Remote
{
  /* The method to be invoked from other JVMs */
  public RetIface compute(TaskIface task) throws RemoteException;
}


/*
 * CompServerClass.java
 */

import java.rmi.*;    /* RemoteException exception */

public class CompServerClass implements CompServerIface
{
  /* Constructor */
  public CompServerClass() {}

  /* The method to be invoked from other JVMs */
  public RetIface compute(TaskIface task) throws RemoteException
  {
    System.out.println("Running received task...");
    return task.run();
  }
}

There is only one more class needed at the server side,
which creates, exports, and registers the server, "as usual"
in RMI

/*
 * StartCompServer.java
 */

import java.rmi.server.UnicastRemoteObject;  /* exportObject */
import java.rmi.Naming;                      /* rebind       */


public class StartCompServer
{
  public static void main (String args[])
  {
    try{
      /* Create ("start") the object which has the remote method */
      CompServerClass robject = new CompServerClass();

      /* Obtain something to export to te client side... the "stub"... */
      CompServerIface riface  = (CompServerIface) UnicastRemoteObject.exportObject(robject, 0);

      /* Register the object on the rmiregistry */
      Naming.rebind("remote", riface);
    } catch (Exception e)
    {
        System.out.println("Hey, something is missing");
        e.printStackTrace();
        System.out.println(e.getMessage());
    }
  }
}

The files (classes and interfaces) needed at the server side
are
   CompServerIface.class
   CompServerClass.class
   StartCompServer.class
   RetIface.class
   TaskIface.class

and, also, the classes RetClass.class and TaskClass.class
should be also available via http o ftp server to the server
side.

The client side is almos complete, since TaskClass and RetClass
are already defined. There is only one more class needed at the
client side, which creates and sends a task to the server,
"as usual" in RMI

/*
 * SendTask.java
 */

import java.rmi.Naming;                    /* lookup         */
import java.rmi.registry.Registry;         /* REGISTRY_PORT  */

import java.net.MalformedURLException;     /* Exceptions...  */
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class SendTask
{
  public static void main(String[] args)
  {
    /* Look for hostname and msg length in the command line */
    if (args.length != 1)
    {
      System.out.println("1 argument needed: (remote) hostname");
      System.exit(1);
    }

    try {
      String rname = "//" + args[0] + ":" + Registry.REGISTRY_PORT + "/remote";

      CompServerIface remoteobj;
      remoteobj = (CompServerIface) Naming.lookup(rname);

      TaskClass oneTask = new TaskClass();

      RetClass retval;
      retval = (RetClass) remoteobj.compute(oneTask);

      System.out.println("Done with return value = " + retval);
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (RemoteException e) {
        e.printStackTrace();
    } catch (NotBoundException e) {
        e.printStackTrace();
    }
  }
}

The files (classes and interfaces) needed at the client side
are
   CompServerIface.class
   TaskIface.class
   TaskClass.class
   RetIface.class
   RetClass.class
   SendTask.class

Now, the server is started as in the first example of this
section:

  java -Djava.security.policy=java.policy -Djava.security.manager \
       -Djava.rmi.server.codebase=http://localhost/distrclasses/ \
       StartCompServer

and this console will not be available for anything else. Remember
that rmiregistry as well as the JVM running StartCompServer should
have access to the "server side" classes, those copied in the server
directory as well as the java.policy file. Remember to make available
the classes to be dynamically downloaded (TaskClass.class and
RetClass.class). The client should be started on another console
with

  java SendTask localhost

The compute server Java RMI example is complete now, and the
server downloads at runtime two classes: TaskClass and RetClass via
the "Java RMI environment". The complete code can be found in
downloadatserver_compserver.tar, including the client and
server side directories with the corresponding .class files and
some scripts to run the applications. Classes TaskClass.class and
RetClass.class have also been uploaded at

  http://ftinetti.googlepages.com 

thus, the server could be started with

  java -Djava.security.policy=java.policy -Djava.security.manager \
       -Djava.rmi.server.codebase=http://ftinetti.googlepages.com/ \
       StartCompServer

This compute server differs from that given in [3] in
   a) The object returned by the method run() of class TaskClass
      is serializable (RetClass class extends Serializable).
      Furthermore, it seems to be reasonable that every object to
      be transferred on the network is an instance of a
      serializable class (i.e. a class which extends
      Serializable) instead of just being an Object instance as
      in [3].
   b) The compute server do not extends UnicastRemoteObject. There
      are not any reasons given in [3] by which the server class
      should extend UnicastRemoteObject.
   c) The server class CompServerClass do not have a main() method.
      Instead, there is one more class, StartCompServer, with only
      one method, main(), which creates, exports, and registers the
      server. Also, the way in which StartCompServer creates,
      exports, and registers the server is a little bit different
      from that given in [3].

3.3.- The third example: mobile agents

The first version of mobile agents in this document will be similar
to that described in [4] (code in [5]), which do not have class
download at all. Basically, mobile agent allows code and data to
migrate from a computer to another on a decision made on the mobile
agent itself. There are many specific efforts to implement mobile
agents, and the examples in this document are given in terms of
Java RMI, which lets to "simulate" the mobile agent behavior rather
easily. The Java RMI server side is almost the same, with an
interface as RemoteIface.java

/*
 * RemoteIface.java
 */

import java.rmi.*;    /* Remote class and RemoteException exception */

public interface RemoteIface extends Remote
{
  /* The method to be invoked from other JVMs */
  public void receive(AgentClass agent) throws RemoteException;
}

i.e. the server should receive an object of class AgentClass,
which will simulate the mobile agent on the distributed system. The
implementation to this class is rather simple: just invoke (locally)
a method like run() on the received object, which should be defined
and implemented in class AgentClass. Thus, the implementation of
RemoteIface could be as simple as this RemoteClass, in the file
RemoteClass.java

/*
 * RemoteClass.java
 */

import java.rmi.*;    /* RemoteException exception */

public class RemoteClass implements RemoteIface
{
  /* Constructor */
  public RemoteClass() {}

  /* The method to be invoked from other JVMs */
  public void receive(AgentClass agent) throws RemoteException
  {
    System.out.println("Agent received");
    agent.run();
  }
}

The class which creates, exports, and registers an object of
RemoteClass is also known at this point of this document,
in the file StartRemoteObject.java

/*
 * StartRemoteObject.java
 */

import java.rmi.server.UnicastRemoteObject;  /* exportObject */
import java.rmi.Naming;                      /* rebind       */


public class StartRemoteObject
{
  public static void main (String args[])
  {
    /* There should be one command line param.: the name to register */
    if (args.length != 1)
    {
      System.out.println("1 argument needed: name to register");
      System.exit(1);
    }
   
    try{
      /* Create ("start") the object which has the remote method */
      RemoteClass robject = new RemoteClass();

      /* Obtain something to export to te client side... the "stub"... */
      RemoteIface riface  = (RemoteIface) UnicastRemoteObject.exportObject(robject, 0);

      /* Register the object on the rmiregistry */
      Naming.rebind(args[0], riface);
    } catch (Exception e)
    {
      System.out.println("Hey, something is missing");
      e.printStackTrace();
      System.out.println(e.getMessage());
    }
  }
}

The only difference of this StartRemoteObject taking into
account previous ones, is that the name to be registered is
not hardcoded but is received as a command line parameter.
Besides being a little bit more general, receiving the name
to be registered as a command line parameter is necessary
in this example in order to jave several JVMs on the same
computer, and each JVM-StartRemoteObject registers a
(unique) name. In other words, there will be several "server
side objects", and each one of them "represents" a host in
a distributed system. Thus, there will be only one
rmiregistry on the computer and several JVMs, where each JVM
will contain a Java RMI server and the server will be
registered with a unique name (the rmiregistry will complain
otherwise).

Concepts behind this code were already clear before starting
the mobile agent example. The "new" ideas (if there is
something new at all) are found in the implementation of
class AgentClass

/*
 * AgentClass.java
 */

import java.io.Serializable;               /* To implement...   */

import java.rmi.Naming;                    /* lookup            */
import java.rmi.registry.Registry;         /* REGISTRY_PORT     */

import java.util.Vector;


public class AgentClass implements Serializable
{
  private Vector<String> hostList;    // List of hostnames to go to
  private int            hostIndex;   // Host to visit next (from hostList)

  /*******************************************************/
  /* Assing the list of hostnames at agent creation */
  public AgentClass(Vector<String> hl)
  {
    hostList  = hl;
    hostIndex = 0;
  }

  /*******************************************************/
  /* The code to run on each host */
  public void run()
  {
    System.out.println("Agent running...");

    // Local processing should be here
    try {Thread.sleep(3000);} catch (Exception e) {}

    // Migrate mobile agent or stop running
    if (hostIndex < hostList.size())
    { // There is at least one more host to run on
      String nextHost = (String) hostList.elementAt(hostIndex);
      String rname = "//" + nextHost + ":" + Registry.REGISTRY_PORT + "/host" + hostIndex;
      hostIndex++;

      System.out.println("About to lookup for " + rname);

      try {
        RemoteIface remoteHost;
        remoteHost = (RemoteIface) Naming.lookup(rname);

        remoteHost.receive(this);
      }
      catch (Exception e) {
        System.out.println("Exception on lookup " + e);
      }
    }
    else
    {  // There is nothing else to do
      System.out.println("No more migrations for this Agent");
    }
  }
}

There are two important details in this class:
   a) The agent carries everything it needs to run on every host
      and, also, to migrate from host to host. Vector hostList
      contains every hostname on which the agent has to run, and
      the name under which the servers are registered are known
      a priori by the mobile agent (and should be agreed on the
      server side). Furthermore, the integer value hostIndex is
      used to point to the next host to visit as well as to
      control the end of the mobile agent run/migrations.
   b) The agent itself decides how, when, and where to migrate.
      Most of these tasks have already been explained before,
      except for the line
                         remoteHost.receive(this);
      which implies that the mobile agent is sendind itself to
      the server side. Note that this line implies that the
      mobile agent loses the thread of control by the semantic
      of a Java RMI, which is the same as that of an invocation:
      control is returned only when the invocations ends. The
      server is just responsible for receiving the agent, which
      includes the invocation to method run() on the agent, since
      this implies that the agent continues "alive".

At this point, there is only one more task to do: a class that
creates an agent and "starts" the agent running. This class could
be defined in a file StartAgent.java as

/*
 * StartAgent.java
 */

import java.util.Vector;


public class StartAgent
{
  public static void main(String[] args)
  {
    /* Look for hostname and msg length in the command line */
    if (args.length == 0)
    {
      System.out.println("Command line parameters needed: hosts to run on");
      System.exit(1);
    }

    Vector<String> hostList = new Vector<String>();

    for (int i = 0; i < args.length; i++)
      hostList.addElement((String) args[i]);

    AgentClass agent = new AgentClass(hostList);

    agent.run();
  }
}

There is no more than the creation and the initial invocation to
run() to an object of class AgentClass. This is a little bit
different from the approach on [4], where the client looks for
and sends the agent to the first server. Summarizing classes and
distributed application startup:
   a) Classes needed on the server side are
         AgentClass.class
         RemoteClass.class
         RemoteIface.class
         StartRemoteObject.class
      i.e. the "three classical" classes for the server side
      (RemoteClass, RemoteIface, and StartRemoteObject) and the
      mobile agent class, AgentClass (there is not dynamic class
      download in this example).
   b) Classes needed on the client side are
         AgentClass.class
         RemoteIface.class
         StartAgent.class
      i.e. the "three classical" classes for the client side
      without dynamic class download.
   c) Running several servers on a host is possible provided the
      rmiregistry is running and every server registers a unique
      name. Starting two servers on its own console could be like
         $> cd <server side code>
         $> rmiregistry &
         $> java StartRemoteObject host0
      on another terminal
         $> cd <server side code>
         $> java StartRemoteObject host1
      and, in fact these last two commands could be repeated as
      many times as servers needed to be running (and host2, host3,
      ... hostn should be used as the names to register on each
      subsequent server).
   d) The client side is simple: just give the hostnames on which the
      mobile agent has to run. For two servers runnign on the local
      host, the commands on a terminal should be
         $> cd <client side code>
         $> java StartAgent localhost localhost

If the servers are running on different hosts, the name registered on
each host could be the same and the hardcoded line with the string to
make the lookup on AgentClass.java could be
      String rname = "//" + nextHost + ":" + Registry.REGISTRY_PORT + "/server";
but it is not essentially different from previous code.

Before discussing a mobile agent with dynamic class download, several
conceptual and implementation differences of this mobile agent from
the approach proposed in [3] deserve some discussion:
   a) The approach in [3] is similar to that in [4] for the client
      side: the client looks for a server and sends a mobile agent.
      The client in this document just creates and "startups" an
      agent and the agent takes every decision on runtime, including
      migration and running several code on other hosts.
   b) The approach in [3] proposes to create a thread in every server
      in order to run the mobile agent locally. The server proposed
      in this document just runs the mobile agent as soon as the
      mobile agent is received. The advantages of the approach in
      this document
         b.1) No explicit creation and handling of threads for mobile
              agents, simpler code.
         b.2) It's clear that mobile agents are simulated using Java
              RMI, i.e. the method invoked remotely does not complete
              until returned. The client ends running only when the
              run() method returns, which happens after the mobile
              agent has ran and returned from every server. There is
              an implicit "call chain" of remoteHost.receive(this)
              with their corresponding return executed before the
              client ends.
      And there are some disadvantages too, at least A single call
      to remoteHost.receive(this) does not end until the agent.run()
      ends. If a thread is created inside the receive(...) method
      which will invoke  agent.run(), then the receive(...) method
      ends and there is not pending return until every method returns,
      as explained in b.2). However, there will be as many threads
      waiting for results as pending returns. Also, on the Sun JVM
      implementations every remote method is executed by an
      independent thread, so the approach in this document is
      basically the same as that of [3] without using explicit
      threads.

The complete code for the mobile agent without class download can be
found in agent_nodownload.tar, including the client and server side
directories with the corresponding .class files and some scripts
and/or instructions to run the applications.

Dynamic class download lets the mobile agent to run code obtained at
runtime instead of having the code in advance/statically. Moreover,
dynamic class download allows running completely different code on
each host where the agent is received for two reasons:
   a) The code could change from one download to the next in time.
      Thus, while the first host downloads a .class file at time
      t1, the second host downloads the same .class file at time t2,
      and the .class file could be changed/updated between t1 and
      t2.
   b) Each JVM downloads code from the site/server independently of
      every other JVM, since each JVM is "instructed" to download
      code via the option -Djava.rmi.server.codebase=... Thus, while
      a JVM could download classes from localhost, while others from
      some "public" ftp or http server. Of course, configuring
      different sources for diferent hosts could produce other
      issues and/or complex maintenance policies.

Dynamic class download for the mobile agent given in the example is
rather straightforward. The class to be dynamically downladed is,
clearly AgentClass. Class AgentClass should have an interface just
like the one given in AgentIface.java

/*
 * AgentIface.java
 */

import java.io.Serializable;               /* To extend...   */


public interface AgentIface extends Serializable
{
  /* The method to invoke locally at each host */
  public void run();
}

Thus, the AgentClass should be changed to implement this interface,
as in the "new" AgentClass.java

/*
 * AgentClass.java
 */

// import java.io.Serializable;               /* To implement...   */

import java.rmi.Naming;                    /* lookup            */
import java.rmi.registry.Registry;         /* REGISTRY_PORT     */

import java.util.Vector;


public class AgentClass implements AgentIface
{
  private Vector<String> hostList;    // List of hostnames to go to
  private int            hostIndex;   // Host to visit next (from hostList)

  /*******************************************************/
  /* Assing the list of hostnames at agent creation */
  public AgentClass(Vector<String> hl)
  {
    hostList  = hl;
    hostIndex = 0;
  }

  /*******************************************************/
  /* The code to run on each host */
  public void run()
  {
    System.out.println("Agent running...");

    // Local processing should be here
    try {Thread.sleep(3000);} catch (Exception e) {}

    // Migrate mobile agent or stop running
    if (hostIndex < hostList.size())
    { // There is at least one more host to run on
      String nextHost = (String) hostList.elementAt(hostIndex);
      String rname = "//" + nextHost + ":" + Registry.REGISTRY_PORT + "/host" + hostIndex;
      hostIndex++;

      System.out.println("About to lookup for " + rname);

      try {
        RemoteIface remoteHost;
        remoteHost = (RemoteIface) Naming.lookup(rname);

        remoteHost.receive(this);
      }
      catch (Exception e) {
        System.out.println("Exception on lookup " + e);
      }
    }
    else
    {  // There is nothing else to do
      System.out.println("No more migrations for this Agent");
    }
  }
}

where

// import java.io.Serializable;               /* To implement...   */
...
public class AgentClass implements AgentIface

are the only two lines changed to the "previous" AgentClass.java,
because the mobile agent does not change and, thus, the code remains
almost the same. Code at the server side is a little bit different,
since it should use the interface, as in the "new" RemoteIface.java
and RemoteClass.java

/*
 * RemoteIface.java
 */

import java.rmi.*;    /* Remote class and RemoteException exception */

public interface RemoteIface extends Remote
{
  /* The method to be invoked from other JVMs */
  public void receive(AgentIface agent) throws RemoteException;
}


/*
 * RemoteClass.java
 */

import java.rmi.*;    /* RemoteException exception */

public class RemoteClass implements RemoteIface
{
  /* Constructor */
  public RemoteClass() {}

  /* The method to be invoked from other JVMs */
  public void receive(AgentIface agent) throws RemoteException
  {
    System.out.println("Agent received");
    agent.run();
  }
}

Classes StartRemoteObject and StartAgent can be used without changes
taking into account the example above, i.e. (copied here for the
sake of completeness)

/*
 * StartRemoteObject.java
 */

import java.rmi.server.UnicastRemoteObject;  /* exportObject */
import java.rmi.Naming;                      /* rebind       */


public class StartRemoteObject
{
  public static void main (String args[])
  {
    /* There should be one command line param.: the name to register */
    if (args.length != 1)
    {
      System.out.println("1 argument needed: name to register");
      System.exit(1);
    }
   
    try{
      /* Create ("start") the object which has the remote method */
      RemoteClass robject = new RemoteClass();

      /* Obtain something to export to te client side... the "stub"... */
      RemoteIface riface  = (RemoteIface) UnicastRemoteObject.exportObject(robject, 0);

      /* Register the object on the rmiregistry */
      Naming.rebind(args[0], riface);
    } catch (Exception e)
    {
      System.out.println("Hey, something is missing");
      e.printStackTrace();
      System.out.println(e.getMessage());
    }
  }
}


/*
 * StartAgent.java
 */

import java.util.Vector;


public class StartAgent
{
  public static void main(String[] args)
  {
    /* Look for hostname and msg length in the command line */
    if (args.length == 0)
    {
      System.out.println("Command line parameters needed: hosts to run on");
      System.exit(1);
    }

    Vector<String> hostList = new Vector<String>();

    for (int i = 0; i < args.length; i++)
      hostList.addElement((String) args[i]);

    AgentClass agent = new AgentClass(hostList);

    agent.run();
  }
}

There are minor differences in running the application is found at
the server side (all of these details have been seen in a previous
section of this document, but repeated here for the sake of
completeness):
   a) The JVM should have a security manager, as that created at
      JVM startup with the option
      -Djava.security.manager
   b) The security manager needs a policy file just like the file
      java.policy containing
      grant {
        permission java.net.SocketPermission "*:1099",
          "connect, accept, resolve";
        permission java.net.SocketPermission "*:1024-65535",
          "connect, accept";
        permission java.net.SocketPermission "*:80",
          "connect";
      };
      and given to the security manager at startup with the option
      -Djava.security.policy=java.policy
   c) An URL has to be given to the Java RMI environment for dynamic
      class download with the option -Djava.rmi.server.codebase as
      in
      -Djava.rmi.server.codebase=http://localhost/distrclasses/
      where it's assumed that there is a local http server which
      has classes binaries under the directory distrclasses. In
      this example, there is only one class to be dynamically
      downladed: AgentClass.class.

Summarizing classes, files, and distributed application startup:
   a) Classes and files needed on the server side are
         AgentIface.class
         RemoteClass.class
         RemoteIface.class
         StartRemoteObject.class
         java.policy
      i.e. the "classical" classes for the server side with dynamic
      class download and the policy file for the JVM security
      manager.
   b) Classes needed on the client side are
         AgentClass.class
         AgentIface.class
         RemoteIface.class
         StartAgent.class
      i.e. the "classical" classes for the client side without
      dynamic class download (at server side).
   c) Running several servers on a host is possible provided the
      rmiregistry is running and every server registers a unique
      name. Starting two servers on its own console could be like
         $> cd <server side code>
         $> rmiregistry &
         $> java -Djava.security.policy=java.policy -Djava.security.manager \
            -Djava.rmi.server.codebase=http://localhost/distrclasses/ \
            StartRemoteObject host0
      on another terminal
         $> cd <server side code>
         $> java -Djava.security.policy=java.policy -Djava.security.manager \
            -Djava.rmi.server.codebase=http://localhost/distrclasses/ \
            StartRemoteObject host1
      and, in fact these last two commands could be repeated as
      many times as servers needed to be running (and host2, host3,
      ... hostn should be used as the names to register on each
      subsequent server). It is assumed that every class to be
      dynamically downloaded is available at
      http://localhost/distrclasses/
   d) The client side is simple: just give the hostnames on which the
      mobile agent has to run. For two servers running on the local
      host, the commands on a terminal should be
         $> cd <client side code>
         $> java StartAgent localhost localhost

The complete code for the mobile agent with class download can be
found in agent_downloadathost.tar, including the client and server
side directories with the corresponding .class files and some
scripts and/or instructions to run the applications.

The first mobile agent example (without dynamic class download) is
just a "plain" example for RMI with some minor specific details:
the agent looks for the server and sends itself as a parameter of
the remote method invocation. The secont mobile agent example (with
dynamic class download) just adds the dynamic class download
configuration. Note that most of the differences of the mobile agent
with class download taking into account the mobile agent without
class download are at configuration level. Furthermore, the code for
every class and new code is straightforward.



 

References


[1] Remote Method Invocation Home
http://java.sun.com/javase/technologies/core/basic/rmi/index.jsp

[2] Getting Started Using Java RMI
http://java.sun.com/j2se/1.5.0/docs/guide/rmi/hello/hello-world.html

[3] Remote Method Invocation - Distributed Computing for Java
http://java.sun.com/javase/technologies/core/basic/rmi/whitepaper/index.jsp

[4] M. L. Liu, Distributed Computing: Principles and Applications,
ISBN-10 0201796449, Addison Wesley, 2003.

[5] M. L. Liu, Distributed Computing, a textbook
http://users.csc.calpoly.edu/~mliu/book/index.html

[6] jGuru: Remote Method Invocation (RMI)
http://java.sun.com/developer/onlineTraining/rmi/RMI.html

[7] jGuru: Remote Method Invocation (RMI)
http://java.sun.com/developer/onlineTraining/rmi/