MOBILE CODE One possible definition: ``Software that travels on heterogeneous networks, crossing protection boundaries, and automatically executed upon arrival at the destination.'' - Tommy Thorn, ``Mobile Code'' MOBILE COMPUTATIONS ``the notion that a computation starting at some network node may continue execution at some other network node.'' requires mobility of code, control, data, and links. - Luca Cardelli, ``Mobile Computation'' Examples (what moves?): - Postscript (code) - SQL (code) - Java Applets (code) - Safe-TCL (code) - Java RMI (code + data) - Telescript (agents) - Facile (agents) - Obliq (computations) - Kali Scheme (computations) Issues at Programming Language level - Portability - Safety - Security: Confidentiality, Integrity, Availability, Authenticity - Efficiency - Semantics - Level of Explicit Control over location of computation? over location of data? --------- SOME MOBILE APPLICATIONS (Knabe) - Heterogeneous Communication - Reduced Communication - Specialized Protocols - Reduced Server Loads - Enriched Interfaces - Temporary Applications - Intelligent Data (Kali Scheme) - User-level load-balancing of distributed computations - Incremental linking - Parameterized client-server - Long-lived parallel computation - Distributed data mining - Executable content in messages ----------- Distributed/Mobile Code Runtime System Issues - Portable code representations - Marshaling/unmarshaling of transmitted data (``serialization'') - Semantics for remote data: copies or references? - Garbage Collection - Code and type distribution - Server registration and invocation ----------- Java RMI - Plain Java provides code mobility via applets/class loaders, but not data mobility. (It also restricts dynamic replacement of code.) - Client must use static collection of types (with dynamic subclassing, of course). - RMI essentially adds an RPC mechanism, with usual stub generation facilities. - Any (serializable) object can be passed to or returned from remote procedure. - ``Remote'' class instances are communicated by reference to remote procedures; ordinary class instances and base values are copied. - Any class referenced by a received object is automatically loaded. (So with appropriate subclassing, this permits dynamic code replacement.) - Distributed reference-counting GC, tied to ordinary GC for local surrogates. - Separate registration service. -------- Example (from RMI whitepaper): A compute server, which executes arbitrary tasks. - Tasks are defined by: public interface Task extends Serializable { Object run(); } - Access to the compute server is defined by this interface: import java.rmi.*; public interface ComputeServer extends Remote { Object compute(Task task) throws RemoteException; } - A basic server implementation: import java.rmi.*; import java.rmi.server.*; public class ComputeServerImpl extends UnicastRemoteObject implements ComputeServer { public ComputeServerImpl() throws RemoteException { } public Object compute(Task task) { return task.run(); } public static void main(String[] args) throws Exception { // use the default, restrictive security manager System.setSecurityManager(new RMISecurityManager()); ComputeServerImpl server = new ComputeServerImpl(); Naming.rebind("ComputeServer", server); System.out.println("Ready to receive tasks"); return; } } - A client (but don't trust these details!): import java.rmi.*; public class Adder implements Serializable { public static int four() { return 2+2; } } public class MyTask implements Task { string s = "0"; public Object run() { s = "2 + 2 = " + Adder.four(); return s; }; } public class Client { public void main(String[] args) { try { ComputeServer server = (ComputeServer)Naming.lookup("ComputeServer"); Task t = new MyTask(); string answer = (string)server.compute(t); System.out.println(answer); System.out.println(t.s); } catch (Exception e) { System.out.println("Server exception: " + e.getMessage()); } } } -------------------- Obliq (Cardelli, 1995) Prototype-based O-O language. Objects are all network objects. Objects are local to a particular site, and never automatically moved. But references to objects can be transmitted freely from site to site. Computations (closures) can be freely transmitted; their free variables retain their bindings to the original site. Disconnected agents simply lack free variables. Dynamically typed, but could be adapted for static typing. ----- OBJECT = Collection of named FIELDS, containing METHODS, ALIASES, or other values. {x1 => a1, x2 => a2, ..., xn => an} A value field: x => 3 A method field: x => meth(y,y1,..,ym) b end (where y == ``self'') A procedure looks like: proc (y1,...,ym) b end Procedures differ from methods in that (a) Methods can only be activated when contained in objects, since they need a ``self'' (and attempting to extract a method from an object activates it) (b) Procedures are activated by ordinary procedure call, and can be stored and subsequently extracted from a field. An alias field: x => alias y of b end -------------- Examples: let o = {x => meth (s) s.x() end }; let o = { x => 3, inc => meth(s,y) s.x := s.x+y; s end; next => meth(s) s.inc(1).x end;} ; o.x o.x := 0 o.inc(1) o.next() o.next := meth(s) clone(s).inc(1).x end ------------- Concepts: site = address space, contains locations, which contain values. - newly created locations are allocated at current site. threads - as usual value: basic values, objects, arrays, closures. - values can have embedded locations; when such values are transmitted, the embedded locations are replaced by network references. Method computation is always performed at the site of the object containing the method. Operations: selection/invocation: a.x a.x(b1,...,bn); updating/overwriting: a.x := b; cloning : clone(a1,...,an); aliasing : a.x := alias y of b end; ----------- Compute Server example: Server side: var replay = proc() end; -- server cheats! net_export("ComputeServer", Namer, { rexec => meth(s,p) replay := p; p() end, lexec => proc(p) replay := p; p() end }); Client side: let computeServer = net_import("ComputeServer", Namer); var x = 0; computeServer.rexec(proc() x := x + 1 end); -- addition occurs at server -- now x = 1 (at client) -- server could now invoke replay(), setting x = 2 (at client!) Alternative client: (computeServer.lexec)(proc() x := x+1 end); -- now lexec returns a procedure, which is applied at client. -- same net result, however. ------ FACILE, extended by Knabe (1995) - Procedures similar to Obliq's, but no objects. - Procedures must be ``agents,'' i.e., must not have free variables, except for certain ubiquitous ones (e.g., libraries). Knabe's conclusions: - Move agents, not computations; require encapsulation and explicit call-backs. - Choice of representations matters! - Use (and perhaps send) multiple code representations. - Intepretation and (lazy) compilation can both be worthwhile. - Need mechanism to distribute types and ubiquitous values. -------