This work begins from the assumption that it is not possible to build abstractions or services that hide this underlying reality, if only because those constraints are physical. The management of mobile objects under these constraints implies that a set of events must be watched, managed or created; others might disappear and won't need further attention. Best effort is provided to react to and exploit fully the current environment to the benefit of interacting mobile and stationary objects.
The main contributions of this work are the design of an architecture that provides the following functionality:
Each language mapping performs two basic functions:
A binding also contains a description of the interactions between its roles. In Hector, interfaces, roles and interactions are all described as input/output messages associated with a finite state machine describing the behaviour of those messages.
Bindings are described as types and are stored in a repository server (type service). A binding does not make any assumptions about communication protocols nor the location of interfaces. Thus it uses different available services to provide transparencies (naming ...).
A binding negotiation precedes any object interactions. This allows participants (objects) in the binding to negotiate QOS requirements, the type of the interaction, underlying protocols needed to implement the desired binding type. All these aspect are known as the binding type.
The initiator of the binding proposes a set of binding templates (with a set of interface references), of which one is eventually chosen once all mandatory roles are fulfilled by object interfaces.
Apart from the pattern of interaction, the following aspects of computing must be also negotiated:
Mobile components cannot guarantee QoS as stationary networks do. Nevertheless a mobile host can provide information about the environment in which it runs. An object runs in a specific type of environment that we call CoE (Class of Environment)[8]. The environment of a mobile object changes dynamically and corresponds to the quantified schema of configuration of local entities that it needs to process an interaction.
Example: if bandwidth < 9 Kbps do_something.Adaptation rules are mainly specified by the application user using an ECA (Event Condition Action) structure. The Event Notification service is responsible for notifying the occurrence of events that an object has subscribed to.
The types of event/conditions can be: crossing a threshold, changes of configuration such as the migration of objects (instance), availability of memory, network disconnection, any types of fault. The types of actions are known as callbacks and can do nothing but notify the application object (exception), defer communications, keep track of instance of moving object, call appropriate functions, changing dynamically actions locally or remotely.
Event/conditions of an adaptation rule are expressed in a constraint language which provides a set of operators which allow arbitrarily complex expressions involving properties and values[9]. This description can be computationaly complete with respect to the observable properties of the local system if the programmer knows all observable events.
An action is fired when the condition expression is true[9]. The following example contains four adaptation rules:
adaptation_rules = {Our adaptability rules syntax are generic enough so that we can express complicated policies such as priority of degradation for multimedia applications. For example, it allows us to specify which aspect of the multi-media stream we want to be degraded by priority when a range of events happen such as dropping the sound first then the number of frames per second).
``bandwidth > 20'':increase_traffic_cb,
``exists(tcp_errors)'': use_udp,
``cpu_load > 10'': migrate_object_cb,
``disconnected'': re_negotiate_cb
}
The support for mobility is build around the Event Notification Service [9](elvin) that make sure that the event condition part of adaptability rules are correctly checked and appropriate actions are triggered. The action part of adaptability rules might be provided by any available service such as the migration service, or directly provided by an interacting object.
import elvin # Notification service
class Migration:
``````Initialise an event notification and watch
migrating events''''''
def __init__ (self):
self.lv = elvin.elvin()
self.lv.subscribe(``exists(migrated)'':chain_update)
def move (self, obj, here, there):
``````suspend and copy the object''''''
p_obj= obj.suspend()
send (p_obj, here, there)
def suspend(self):
``````suspend and pickle an object with contexts''''''
T_id = get_reftype(obj)
bind_contex = get_bind_context(obj)
p_obj = pickle (obj, T_id, bind_context)
notify(``migrating'', obj.__name__, there)
def resume(self, p_obj):
``````retrieve instance of object''''''
obj, T_id, bind_contex = unpickle(p_obj)
factory (Tid, obj, bind_context)
notify(``migrated'', obj.__name__, there)
def chain_update (self,d_not):Upon a decision to migrate an active object, the migration service does:
``````update reference''''''
if in_current_capsule(obj):
-- if there is a chain_in pointer to this
-- object in the capsule
create_chain_out(obj, there)
update (trader, name_service, obj,there)
Suspend the execution of a running object. Get its type reference (Tid) description template from the type service so that the factory mechanism can have a description of this object at the other end. Pickle the object with information about the current context and the Tid. The context contains information about the current binding, the capsule, connections, and the current state. Notify the new location of the object to the event notification service so that those who are subscribed to migration events can be aware of.
creates a chaining mechanism ``chain_update'' to forward requests to the new location (chain_in, chain_out). Sends the object.
All capsules are notified about the migration of an object and their respective naming and trading services will update all relevant information about references of migrated object. This event flushes the service cache entries.
Resume the object to retrieve ref_type and obj_state. The type service is used to retrieve the template with the ref_type. obj_state, ref_type and context that are used to run it in a specific environment.
When a migrated object finishes its execution or when the binding ends then all forwarded infrastructure is garbage collected.
A function can be directly moved without informing the type service. A function will be evaluated by the receiving object. Such functionality allows us to the redefinition dynamic of the contents of a method or function within a targeted object. Such functionality is very useful in the following scenario: a video consumer with a poor CPU and screen capacity wants to receive a video from a producer. The consumer cannot exploit all of the data provided by the producer and wants to filter the stream. Thus, the consumer sends a filter (function) to the video producer based on its video definition support. The producer will send a filtered (reduced) amount of data thus sparing the bandwidth instead of having the consumer filtering incoming data and degrading its already poor CPU capacity.
In the Python world, before functions and classes can be migrated, they first have to be pickled. In standard Python, functions cannot be pickled at all (their code objects can be marshalled, but this does not provide sufficient information to allow a function to be re-created at a remote host), and pickled classes merely provide a reference to the module that they were imported from (meaning that source and target sites must either share a file system or at least mirror the same structure for module import purposes). To overcome these problems we create Python classes to represent fully-picklable functions and classes - FlatFunction and FlatClass (the implementation of FlatFunction is shown below). The real work of these modules is provided by an extension module `internals' that provides access to the Python C API (e.g. Py_FunctionNew). Now, given these modules we can pickle a class and its methods, and re-create that class at the target host even if the target host previously had no knowledge of the class (obviously, this raises some security concerns which are outside the scope of this paper).
`````` A class to represent fully `picklable' functions. ``````
# Standard/built-in modules.
import marshal
# Common modules.
import internals
class FlatFunction:
`````` A class to represent fully `picklable' functions. ``````
def __init__(self, fn=None):
`````` Create a picklable representation of a function
We use a default argument here so that the flattened
function can be pickled/unpickled without the need for a
`__getinitargs__' method. We do this so that the
flattened function does not have to store any reference
to the function object that it was created from.''''''
# The only time `None' is a valid argument is when
# the constructor is being called by the `pickle' module
if fn != None:
self._flatten(fn)
return
def unflatten(self, namespace=None):
`````` Create a function object from the flattened
function! ``````
if namespace == None:
namespace = globals()
return self._unflatten(namespace)
###########################################################
# Internal methods.
###########################################################
def _flatten(self, fn):
`````` Create a `picklable' representation of a function.``````
# Get a list of the function's default parameter
# values, its code object, and its documentation.
self.defaults = fn.func_defaults
self.code = marshal.dumps(fn.func_code)
self.doc = fn.__doc__
return
def _unflatten(self, namespace):
`````` Create a function object from the flattened function
# Unmarshal the code object.
code_object = marshal.loads(self.code)
# Create a function object from the code object.
function_object = internals.PyFunction_New \
(code_object, namespace)
# Set the default arguments.
if self.defaults != None:
internals.PyFunction_SetDefaults \
(function_object, self.defaults)
# Set the documentation string.
if self.doc != None:
internals.PyFunction_SetDoc \
(function_object, self.doc)
return function_object
Intuitively, each interacting object is encapsulated in its capsule. A capsule has a reference to each interacting objects. These references are not necessarily location independent. The communication layer acts as an agent for the remote communication. It is responsible for stub generation and forwarding mechanisms. The stub contains the physical location of its interacting objects. Two functions, provided by the communication layer, are used to set up the tracking mechanism. These two functions are available to all migrating objects (by inheritance).They are:
D. Arnold, A. Bond, M. Chilvers - Hector - Distributed Object in Python - Dr. Dobb's Sourcebook #273 January 1997. http://www.dstc.edu.au/Hector/
A. Campbell, G. Coulson, D. Hutchison - A quality of service Architecture - Journal of Computer Communication Review - Vol 24 Number 2 April 1994.
J. Gray, P. Helland, P. O'Neil, D. Shasha - The Dangers of Replication and a Solution - SIGMOD Record Proc, Quebec - June 1996
ITU/ISO - Reference Model for Open Distributed Processing - Part 1,2,3 IS 10746-(1,2,3). http://www.iso.ch:8000/RM-ODP/
Object Management Group - CORBAservices - Life Cycle Services Specification - 1995
http://www.omg.org/
J. Hylton, K. Manheimer, F-L Drake Jnr, B. Warsaw, R. Masse, G. van Rossum - Knowbot programming: System support for mobile agents.In Proceedings of the 5th International Workshop on Object Orientation in Operating Systems (IWOOOS '96), pages 8-13, Oct. 1996
A.Rakotonirainy, A. Bond - Mobile Transparency - Proc ICODP, Toronto, May 1997. http://www.dstc.edu.au/AU/staff/andry/icodp.ps
B. Segall - Elvin: Event notification service. DSTC Internal Report, 1995.
http://www.dstc.edu/Elvin/