LINGUISTIC CONSTRUCTS FOR OBJECT-ORIENTED MOBILE CODE PROGRAMMING & THEIR IMPLEMENTATIONS

public void execute() throws KlavaException This method will be invoked when a process is executed (just like run for threads). A process must be executed within a node, which will be its execution environment [Carzaniga et al., 1997]. KlavaPro ess also offers all the methods to access tuple spaces; these methods transparently call the corresponding methods of the class NetNode. In the new hierarchical model of KLAIM, a new category of processes is introduced (Section 4.1.2) that, apart from the standard operations, can also perform the privileged actions dealing with node connectivity implemented by the methods presented in Section 7.1. 4.2. For these privileged processes KLAVA provides a subclass of the class KlavaPro ess, namely NodeCoordinator. As required by the model, due to security reasons, node coordinators cannot migrate, and cannot be part of a tuple. Of course, in order to guarantee better programmability, this rule is slightly relaxed: a node coordinator can perform the eval of a node coordinator, provided that the destination is self. Since processes and node coordinators have to forward the KLAIM actions to the node they are currently executing on, they need to store a reference such node. In order to force the distinction between standard processes (that can only perform a limited number of actions) and node coordinators (that can also execute privileged actions), the reference is indeed a reference to a proxy that has a reduced interface when it is stored inside a standard process: a NodeProxy is stored inside a standard process, while a “more powerful” NetNodeProxy is stored in a node coordinator. Notice that since a proxy, and not a direct reference to the actual node, is stored, the process cannot call a method that is not allowed to call, since the proxy itself does not provide it: misuses, thanks to the different interfaces of proxies, are caught at compile time. This pattern Proxy [Gamma et al., 1995], is also used in the implementation of the Aglets [Lange &Oshima, 1998] to enforce run-time security restrictions. A number of standard node coordinators is supplied by KLAVA, that simply perform the connection actions, such as, LoginNodeCoordinator, Subs ribeNodeCoordinator, etc., and that performs the corresponding complementary actions, such as, A eptNodeCoordinator, RegisterNodeCoordinator, etc. In particular the old KLAVA classes Node and Net are still supplied in the framework, implemented as subclasses of NetNode that simply execute a LoginNodeCoordinator and an A eptNodeCoordinator, respectively. In the class Net such coordinator executes an infinite loop, always accepting new login requests. This also enables the old KLAVA applications to seamlessly scale to the new hierarchical model. 7.2 Code mobility in KLAVA In KLAVA, processes can be sent as part of a message and executed at the destination site, where however their Java classes, i.e., their code, may be unknown. It might then be necessary to make such code available for execution at remote hosts; this can be done basically in two different ways: automatic approach: the classes needed by a process are collected and delivered together with the process; 112 THE JAVA PACKAGE KLAVA Code mobility in KLAVA on-demand approach: when a Java class is needed by the remote computer that received a process for execution, it is requested to the server that did send the process. We follow the automatic approach because it complies better with the mobile agent paradigm: during a migration, an agent takes with it all the information that it may need for later executions. The drawback of this approach is that code that may never be used by the mobile agent or that is already provided by the remote site is also shipped. However, our choice has the advantage of simplifying the handling of disconnected operations [Park & Reichl, 1998]: the agent owner does not have to stay connected after sending the agent and can connect later just to check whether his agent has terminated. This may not be possible with the on-demand approach: the server that sent the process must always be on-line in order to provide the classes needed by remote hosts. Therefore, a process must be sent along with its class binary code, andwith the class code of all the objects the process uses. Obviously, only the code of user defined classes has to be sent, as the other code (e.g., Java and Klava classes) is common to every KLAVA application. This guarantees that classes belonging to java sub-packages are not loaded from other sources (especially, the network); this would be very dangerous, since, in general, such classes have many more access privileges. All the nodes that are willing to accept remote processes (due to security issues, a node may refuse accepting remote processes for execution) must have a custom class loader: a NodeClassLoader supplied by the KLAVA package. When a process is received from the network, before using it, the node must add the class binary data (received along with the process) to its class loader’s table. During process execution, whenever a class code is needed, if the class loader does not find the code in the local packages, then it can find it in its own local table of class binary data. The names of user defined classes can be retrieved by means of class introspection (Java Reflection API). Just before dispatching a process to a remote site, a recursive procedure is called for collecting all classes that are used by the process when declaring: data members, objects returned by or passed to amethod/constructor, exceptions thrown bymethods, inner classes, the interfaces implemented by its class, the base class of its class. This procedure is implemented by some methods of the class KlavaPro esspresented in Listing 7.3 and 7.4 in a simplified form. Notice that this procedure recursively calls itself. Mutual dependencies are handled by checking whether a class has already been collected; thus a class is never inspected more than once. When extending KlavaPro ess, there is an important detail to know in order to avoid runtime errors that would take place at remote sites and would be very hard to discover: Java Reflection API is unable to inspect local variables of methods. This implies that if a process uses a class only to declare a variable in a method, this class will not be collected and thus, when the process executes that method on a remote site, a ClassNotFoundEx eptionmay be thrown. This limitation is due to the specific implementation of Java Reflection API, but it can be easily dealt with, once the programmer is aware of the problem. The programmer can explicitly add a class to the collection by calling the method addUsedClass in Listing 7.4; all the details of code marshaling are still handled by KLAVA and are transparent to the programmer. The X-KLAIM compiler will generate code for processes in such a way that all the classes used by the process will successfully be collected by KLAVA (see Section 6.6 and 8.6.3). Once these class names are collected, their byte code is gathered in the first node from which the process was sent, and packed along with the process in a KlavaPro essPa ket object. Notice that the process (its variables) is written in an array of bytes and not in a field of type KlavaPro ess. This is necessary because otherwise, when the packet is received at the remote site and read from the stream, the process would be deserialized and an error would be raised when any of its specific classes is needed (indeed, the class is in the packet but has not yet been read). Instead, by using our representation, we have that, first, the byte code of process classes is read from the packet and stored in the class loader table of the receiving node; then, the process is read from the byte array; when process classes are needed, the class loader finds them in its own table. Thus, when a node receives a process, after filling in the class loader’s table, it can simply THE JAVA PACKAGE KLAVA 113 Code mobility in KLAVA protected void getUsedClasses( Class c ) f if (c == null jj ! addUsedClass( c )) return ; Field[] fields = c.getDeclaredFields() ; Constructor[] constructors = c.getDeclaredConstructors() ; Method[] methods = c.getDeclaredMethods() ; int i ; for( i = 0 ; i < fields.length ; i++ ) getUsedClasses( fields[i].getType() ) ; for ( i = 0 ; i < constructors.length ; i++ ) f getUsedClasses( constructors[i].getParameterTypes() ) ; getUsedClasses( constructors[i].getExceptionTypes() ) ; g for ( i = 0 ; i <methods.length; i++ ) f getUsedClasses( methods[i].getReturnType() ) ; getUsedClasses( methods[i].getParameterTypes() ) ; getUsedClasses( methods[i].getExceptionTypes() ) ; g getUsedClasses( c.getDeclaredClasses() ) ; getUsedClasses( c.getSuperclass() ) ; getUsedClasses( c.getInterfaces() ) ; g protected void getUsedClasses( Class[] classes ) f for (int i = 0 ; i < classes.length ; ++i) getUsedClasses (classes[i]) ; g Listing 7.3: The methods to collect the classes used by a process 114 THE JAVA PACKAGE KLAVA Code mobility in KLAVA protected boolean addUsedClass( Class classVar ) f String className = classVar.getName() ; if ( isUsefulClass( className ) && ! usedClasses.containsKey( className ) ) f usedClasses.put( className, getClassBytes( className ) ) ;