Embedding Remote Object Mobility in Java RMI Marco Avvenuti and Alessio Vecchio Dip. di Ingegneria dell’Informazione, Universit`a di Pisa Via Diotisalvi 2, I-56126 Pisa, Italy fm.avvenuti,
[email protected] Abstract Mobile code offers several capabilities such as bandwidth-efficient communication, disconnected operation, and support for dynamic and flexible systems. Nevertheless, mobile code based programming paradigms have difficulty in flowing from research activities to commercial systems. The main reason seems to be the lack of integration between the language support for mobility and a familiar programming environment. This paper addresses this issue and describes the basic functionalities of MobileRMI, a toolkit that extends Java RMI with mechanisms for creating and moving remote objects across different address spaces. A key feature of MobileRMI is the automatic updating of remote references to mobile objects, necessary to support remote method invocation even in the presence of object mobility. The extensions to Java RMI only affect the semantics of the remote reference layer, while leaving both the transport layer and the bytecode generation process unchanged.
1 Introduction An emerging and promising approach to distributed computing is mobile code – the capability to dynamically change the bindings between software components and the location where they are executed. Well-known mobile code paradigms are code on demand (COD) and remote evaluation (REV). A third approach is based on the mobile agent (MA) concept. A MA is a program that can autonomously migrate from host to host in a network in order to carry out a given task on behalf of a user or an application. To accomplish their goal, MAs exploit locally available resources and collaborate with each other [4]. Although a full understanding of capabilities and limits of these techniques is still missing, it is evident that having control over dynamic relocation of components can greatly simplify the design of applications that deal with user mobility, device heterogeneity, disconnected opera-
tions and other mobile computing related issues. The advantages come up from enhanced flexibility, network traffic reduction, computation off-loading, asynchronous interaction among components. For example, one server’s functionalities can be dynamically extended by shipping new components; network traffic can be reduced by moving code instead of data in applications processing large quantities of data; and the asynchronous nature of MA can facilitate application design when partially connected devices are involved. A more detailed discussion of mobile code capabilities can be found in [6]. Many Java-based mobile code systems have been proposed [1], usually providing a monolithic, agent-oriented programming framework. Despite the richness of services and tools, these systems have difficulty in flowing from research activities to real world applications. A reason seems to be that the use of the mobile agent concept leads developers to a programming model far from the everyday practice, often forcing them to design applications within a not very natural framework. A more practical reason is that any known application which could profitably exploit code mobility, can also be programmed according to a more traditional approach. While waiting for the killer application, if any, we can try to encourage the use of mobile code programming styles by integrating them in a familiar distributed computing framework. The term agent is subject to different interpretations, ranging from AI-oriented to object-oriented ones. Here, we do not deal with agent properties such as autonomy, reactiveness, pro-activeness, or social abilities. Instead, we focus on the mobility-related issues, and consider a mobile agent as an (possibly compound) object with its own execution thread and able to trigger its migration from node to node while carrying code and data in the form of objectspecific properties. According to a bottom-up approach, such a mobile object can be used as a basic, yet powerful abstraction for building mobile code systems [7]. This paper describes the basic functionalities needed to achieve object migration within the Java Remote Method Invocation (RMI) middleware. The aim of our work is to
provide the programmer with a set of mobility primitives embedded in a well-known semantic framework. We designed and implemented the MobileRMI toolkit with the purpose of replacing Java RMI with a mobility-enabled RMI system, including facilities for creating and moving remote objects across different address spaces. The toolkit provides for automatic updating of remote references, necessary to support remote method invocation even in the presence of object mobility.
Client
Server
Stub
Skeleton
Remote Reference
Remote Reference
Transport Layer
Figure 1. The RMI layered architecture
2 Rationale The Java RMI middleware allows distributed objects to communicate by means of method invocation. Remote method invocation is the action of invoking a method on a remote object (also called server) from a client object residing on another Java Virtual Machine (JVM). To invoke remote methods, a client must hold a remote reference to the server. The key features of RMI are the following [9]: 1. a method invocation on a remote object has the same syntax as a method invocation on a local object. 2. the programmer is aware of the differences between local objects interaction and remote objects interaction. As the RMI system establishes a unifying layer across all Java flavors (J2EE, J2SE, and a subset of J2ME), it represents an effective paradigm for building distributed object applications in a heterogeneous setting. Code mobility can be supported in a distributed object computing environment by means of the mobile object concept. Embedding object mobility within Java RMI brings two important advantages: i) the Java distributed object model is enforced also to mobile code based applications, and ii) mobility can be embedded inside a well-known programming environment. Enforcing the Java Distributed Object Model. The Java distributed object model, which establishes the theoretical base of the RMI system, is the primary rationale for having chosen RMI as the starting point of our work. According to this model, even if the syntax of remote method invocation is the same of the one of local method invocation, a semantic distinction is made between local and remote communication:
the programmer is forced to deal with additional exceptions that can be raised by the execution of remote methods. with remote method invocation, the parameter passing mechanism acts differently, depending on the nature of
parameters. If the parameter is a remote object, passing is by reference; if the parameter is a ”normal” object, passing is by copy. The reasons of making the differences between remote and local objects apparent can be found in [10], where the authors argue that distributed systems require the programmer to be aware of issues introduced by the distribution of components, such as latency, concurrency, and partial failures. We argue that the Java distributed object model should be applied also to mobile object systems, since they are distributed object systems. Papering over these characteristics of distribution would lead to systems lacking of robustness and reliability, especially when dealing with Internet-wide systems. Embedding mobility. Many Java-based systems have been proposed in order to support code mobility, most of them focusing on the concept of mobile agent [12]. As pointed out in [8], although building a distributed application as a collection of mobile agents can often be a sound choice, there are situations in which the semantic richness of the agent concept can back-fire on the programmer, who is forced to express low level concepts within a complex framework. In a different approach, the programmer should have at his disposal a set of primitives for object and code mobility, useful to face the particular problem using the most appropriate mobile code design paradigm. But, in order to avoid changing the programming style if mobility is not used, such primitives should be integrated, or even better embedded, in a well-known programming environment. These issues have been addressed in C ODE [8], a Javabased mobile code toolkit designed following the guidelines of flexibility, minimality, non-invasiveness, and extensibility. C ODE is, together with Sumatra [2], the mobile code system that mostly influenced the design of our toolkit. MobileRMI inherits from C ODE the goal of integrating the mobility primitives within the Java language, and takes from Sumatra the idea of interacting with mobile objects by means of method invocation.
public static Remote create(String classname, String host, int port, String codebase) throws RemoteException, RemoteCreationException public void move(String host, int port) throws RemoteException, MigrationException
Figure 2. The create() and move() methods
3 Design issues Java RMI itself supports object mobility, although in a rather indirect way. Objects passed as arguments or results in a remote method invocation are passed by copy, unless they are remote objects. A mobile object system can then be built at the client-server level (Figure 1) of the RMI architecture, using its parameter passing mechanism for transferring objects from a JVM to another. This is done, for example, in rmi64 [5]. However, such mobile objects have the limit that it is not possible to invoke remote methods on them, simply because they are not remote objects. The problem is that RMI prevents a remote object from being serialized and sent between JVMs as a parameter (the object is replaced by its stub). Some simple workarounds for this problem exist: For example, it is possible to include the remote object in a container, then pass the container as the parameter of remote method invocation. This forces the serialization of the remote object implementation and results in the creation of a copy of the object in an other address space. However, such a copy is identified by a different remote reference. This implies that i) the migrated object is no longer reachable by clients holding a reference to the original object, and ii) the migrated object has a different internal status, as the number of referring clients affects the object’s garbage collection data structures. In order to seamlessly integrate object mobility with remote method invocation we needed to address this problem. Our original contribution consists in combining the mechanism for the transport of remote objects with a reference updating mechanism, that makes references held by clients follow the migrating server. This implies that, as described in the next Section, only part of the mobility layer was implemented at the client-server level, while the core implementation was located at the Remote Reference layer (Figure 1).
4 The MobileRMI system In MobileRMI the unit for mobility is the remote object. More in detail, MobileRMI extends the UnicastRemoteObject (URO), a remote object implementation that uses a TCP-based stream protocol for carrying out method invocations from clients to servers. In addition to the functionalities of a standard UnicastRemoteObject, its mobile
extension, the MobileUnicastRemoteObject (MURO), can be created and moved across different JVMs. Remote references to a MURO are updated according to its migration path, so that any client application that holds a reference to it can continue to invoke remote methods. With the MobileRMI add-ons, a client application can create a MURO object on a different JVM by means of the create() method. The signature of the new method implemented by the MURO class is shown in Figure 2, where classname specifies the name of the class implementing the object behavior, host and port identify the remote JVM, and codebase contains the URL from which the class bytecode has to be loaded. The method returns the remote reference to the MURO object. Reference to an existing MURO can be obtained the same way it is done with a standard UnicastRemoteObject, i.e., by using the RMI naming facility. Client
Client
move()
MURO
A
MURO
B
A
B
Figure 3. A client application moves a MURO from A to B The interaction with a MURO takes place through remote method invocation. As shown in Figure 3, a client holding a reference to a MURO can trigger its migration to a different JVM by invoking a move() method on the object reference. The signature of the move() method is shown in Figure 2, where the host and port parameters identify the receiver JVM. It should be noted that autonomous migration is enabled by a MURO invoking a move() on itself.
4.1 Supporting remote creation and migration Remote creation and object migration require the existence of a daemon, say the MobilityDaemon, in the receiver JVM. The MobilityDaemon is a remote object exporting two methods, MDcreate() and MDmove(), which are
c
m
MURO.create(classname, host, port, codebase) md=getStubForMD(host, port); md.MDcreate(classname, codebase); ; ;
(A) (B)
c
o
m
o.move(host, port); ; md=getStubForMD(host, port); md.MDmove(); ; ; ; ; ;
Figure 4. Operations with MobileRMI: A) remote creation, B) migration used for implementing the remote creation and move operations. 4.1.1 Remote creation The static method create() is invoked without an explicit instance of a MURO. As depicted in Figure 4A, when invoked by client c, the method first creates a stub md for the MobilityDaemon m located in the receiver JVM, then invokes the remote method MDcreate() on md. On receiving a request of creation, m creates a new MURO on its address space, exports it, and returns the object’s stub to c. Classes defining the MURO are loaded from codebase by the RMIClassLoader. 4.1.2 Object migration When a client, or the MURO itself, invokes the move() method, the MURO serializes itself and transfers the byte stream to the receiver JVM, where it is reconstructed and made available for method invocations. If the migration is successful, the remote reference held by the invoking object is updated to the new location. From now on, methods are executed in the new address space. If an error occurs, the move() method raises a MigrationException and leaves the MURO on the sender JVM. To overcome the RMI restrictions on parameter marshaling and un-marshaling, which prevent the serialization of remote objects, the migrating MURO hides its serialized state in a container object. In this case, the serialization process is carried out by using a customized extension of ObjectOutputStream, which avoids replacing a remote object with its stub.
As shown in Figure 4B, client c triggers the migration of o to a new JVM. On receiving the invocation of move(), o first creates a container object for its serialized state, then obtains a stub md for the receiver MobilityDaemon m, and invokes MDmove() passing the container as parameter. On receiving a request of migration, m reconstructs the MURO on its address space, exports it, and returns the new reference to o. It should be noted that, like with any URO object, several clients can simultaneously have a reference to a MURO. This may lead to different threads running at the same time in a MURO. In order to avoid inconsistency (e.g., an object is moved by one thread while being modified by another), MobileRMI allows the execution of the move() method only after completion of other methods. The definition of an application-specific synchronization policy among methods other than move() is up to the programmer.
4.2 Class loading The RMI default class loading policy prevents the bytecode to be attached to the parameters of a remote invocation: classes are loaded on-demand by the receiver JVM as they become necessary for continuing the execution. When a class is not available locally, the RMIClassLoader retrieves it from the URL annotated in the codebase property associated to each parameter. With on-demand class loading, fetching of classes can occur with a significant delay from the time of remote creation or migration. This is, of course, not desirable when operating in a partially connected environment, where a class could be requested after the connection went down (e.g., when communication is through dial-up connection
or wireless links). Within such a scenario, a mobile agent’s autonomy would be compromised by introducing a dependency from the codebase. MobileRMI deals with this problem by providing a mechanism for sending the related classes together with a serialized object. This is accomplished by a variation of the move() method that allows to pack, compress and move a MURO together with its class closure. On the receiver JVM, the MobilityDaemon stores the received classes in a local cache.
4.3 Remote reference updating The existence of a remote reference updating mechanism is of primary concern for achieving a seamless integration between object mobility and the remote method invocation paradigm. In order to preserve remote method invocation on a moving server object, it is necessary to update all remote references held by clients which point to that server. The mechanism implemented in MobileRMI is transparent and acts differently depending on the owner of the reference to update. In the case of the reference held by the client which triggered the migration, the updating takes place at the completion of the move() method. To achieve this, we modified the UnicastRef class, which implements the remote reference on the client side. The new version recognizes the move() method and updates the reference as soon as the migration completes successfully. As shown in Figure 4B, after having reconstructed the migrated object o, the MobilityDaemon m returns the new reference to o, which in turn passes it to c. Here it is managed by the UnicastRef instance, leaving the programmer unaware of the process. In the case of references held by clients which did not trigger the migration, the updating takes place when they invoke a method on the migrated server. When a server migrates, it leaves a dummy-object on the departure site. If the server moves several times, a dummy-objects chain allows to reach it. Each dummy-object holds a reference to the next element in the chain, with the last one holding a reference to the ”real” object. When a client uses an out-ofdate remote reference, it actually invokes the method on the dummy-object, which is a remote object itself. On receiving a remote method invocation, a dummy-object returns to the client the remote reference of the next element in the chain. Following the chain, clients that invoke methods on the migrated server will eventually get their remote references updated. The dummy-object chain interacts with the RMI distributed garbage collector with a twofold purpose: i) to update remote references held by clients that do not make explicit method invocations, and ii) to automatically shorten the dummy-object chain. These functionalities were ob-
tained at almost no cost by piggy-backing garbage collection messages. A description of the remote reference updating through garbage collection and its implementation can be found in [3].
5 Programming with MobileRMI MobileRMI has been built starting from the SUN’s RMI implementation, which has been extended at the RMI Remote Reference Layer to implement the new functionalities. As MobileRMI does not require modifications to the Java interpreter (as it is done, instead, in Sumatra), programs portability is preserved. When compiling or executing applications using the MURO class it is only required to put the MobileRMI package in the classpath. As far as the compatibility with standard RMI tools is concerned, the stub classes can be still generated by the default rmic compiler, but a new version of the rmiregistry must be used. A sample program which exploits the MobileRMI toolkit is shown in Figure 5. The program, derived from an example contained in [11], implements a simple client program, ComputeTask, which creates a remote object, ComputeEngine. This is a MURO implementing the Compute remote interface, which in turn defines the executeTask() method. After having created it, the client makes ComputeEngine execute a first task, moves it to another host, and finally makes it execute a second task.
5.1 Mobile agents A mobile agent (MA) can be built as a mobile object with its own execution thread and able to move itself from host to host in a network. The toolkit includes the definition of the MobileAgent class, which extends the MURO definition and defines the go() method, invoked by a MobileAgent object to migrate. As shown in Figure 6, the go() method is simply implemented by a call to the move() method on the object itself, and by a call to the remoteRun() method that, after migration, activates a new thread on the receiver address space. As object mobility is implemented using the standard Java object serialization, MobileRMI can support only weak migration of agents: code and data state are transferred across different JVMs, but no migration of execution state is involved. This means that each time a MA arrives at destination, the execution restarts from a given method, say run(). This method must be implemented by the classes extending the MobileAgent abstract class.
public class ComputeTask { Compute comp; //The handle for a ComputeEngine remote object Task taskA = ; Task taskB = ; String codebase = ; String remoteHost = ; public static void main(String args[]) { ... try { //create a remote ComputeEngine object comp = (Compute)MURO.create("ComputeEngine", remoteHost, Mobile.DEFAULT_PORT, codebase); resul = comp.executeTask(taskA); //Makes ComputeEngine execute taskA comp.move(); //Moves ComputeEngine to another host resul = comp.executeTask(taskB); //Makes the ComputeEngine execute taskB ... } catch (RemoteCreationException r) {...} catch (MigrationException m) {...} catch (RemoteException e) {...} } }
Figure 5. A simple client program using the MobileRMI toolkit public interface RemotelyRunnable { public void remoteRun(Remote m) throws MigrationException, RemoteException; } public abstract class MobileAgent extends MobileUnicastRemoteObject implements RemotelyRunnable, Runnable { private Remote myself; protected MobileAgent() throws RemoteException{ super(); ... myself = toStub((Remote)this); } protected void go(String host,int port) throws MigrationException, RemoteException { ((Mobile)myself).move(host,port); ((RemotelyRunnable)myself).remoteRun(myself); } public void remoteRun(Remote newMyself) throws MigrationException, RemoteException{ myself = newMyself; Thread t = new Thread(this); t.start(); } }
Figure 6. The MobileAgent class pseudo-code
6 Conclusions
References
We described how to embed a mobility layer within the Java RMI middleware, and discussed the benefits resulting from this integration. Our work is successful in showing how it is possible to support mobile code in a nonconventional way: instead of providing a set of mobility libraries on top of the Java programming environment, MobileRMI integrates the mobility primitives inside an existing middleware. The result is standard RMI plus a mobility layer providing basic, yet powerful abstractions enabling migrating applications. Our approach not only makes it possible to move components from a JVM to another by means of a straightforward syntax usually unavailable in other systems, but also guarantees that existing RMI-based applications can be mobility-enabled with little changes. Also, as the RMI system establishes a unifying layer across all Java flavors (J2SE, J2EE, J2ME with RMI profile), MobileRMI inherits the capability to build distributed object applications in a heterogeneous setting. The integration process carried out with the MobileRMI toolkit resulted to be quite smooth, since RMI itself provides a mechanism for moving objects across different JVMs, and makes an extensive use of code mobility (even if in the sole form of dynamic downloading of stub classes and of actual parameters type bytecode). MobileRMI turns the RMI middleware into a support for mobility by means of minor changes: The core MobileRMI implementation consists of a few hundreds of lines of code ”patching” SUN’s implementation of RMI. This is, in our opinion, a good proof of the soundness of choosing the RMI system as development basis for our toolkit. As a concluding remark, it is noticeable to say that timeliness of our research, other than for its contribution to the spread of mobile code techniques in ”ordinary” networked environment, is furtherly motivated by the coming near of radically different environments, such as those provided by mobile ad hoc networks, that pushes the need of alternative programming paradigms and paves the way for further investigation about mobility of software components.
[1] The mobile agent list. http://mole.informatik.uni-stuttgart.de/mal/mal.html. [2] A. Acharya, M. Ranganathan, and J. Saltz. Sumatra: A Language for Resource-aware Mobile Programs. In Mobile Objects Systems: Towards the Programmable Internet, volume 1222, pages 111–130. Lecture Notes in Computer Science, Apr. 1997. [3] M. Avvenuti and A. Vecchio. Supporting Remote Reference Updating through Garbage Collection in a Mobile Object System. In Proceedings of the 9th Euromicro Workshop on Parallel and Distributed Processing (PDP2001), Mantova, Italy, February 2001. IEEE Press. [4] A. Fuggetta, G. P. Picco, and G. Vigna. Understanding Code Mobility. IEEE Transactions on Software Engineering, 24(5):342–361, May 1998. [5] T. Gschwind. Rmi64. Technical report. http://www.infosys.tuwien.ac.at/Staff/tom/Projects/rmi64. [6] C. G. Harrison, D. M. Chess, and A. Kershenbaum. Mobile agents: Are they a good idea? Technical report, T. J. Watson Research center, IBM Research Division, March 1995. [7] E. Jul, H. Levy, N. Hutchinson, and A. Black. Fine-grained mobility in the Emerald system. ACM Transactions on Computer Systems, 6(1):109–133, Feb. 1988. [8] G. P. Picco. C ODE: A Lightweight and Flexible Mobile Code Toolkit. In K. Rothermel and F. Hohl, editors, Proceedings of the 2snd International Workshop on Mobile Agents (MA’98), volume 1477, Stuttgart, Germany, Sept. 1998. Lecture Notes in Computer Science. [9] Sun Microsystems. Java Remote Method Invocation Specification, October 1998. [10] J. Waldo and al. A note on distributed computing. Technical Report 94-29, Sun Microsystems, November 1994. [11] A. Wollrath and J. Waldo. The Java Tutorial: the RMI trail. Sun Microsystems. [12] D. Wong, N. Paciorek, and D. Moore. Java-based Mobile Agents. Communications of the ACM, 42(3):92–102, Mar. 1999.