Oberon || Library || Module Index || Search Engine || Definition || Module

Ulm's Oberon Library:


RemoteObjects - access of remote objects


(* access mode *)
CONST serial = 0;
CONST parallel = 1;
(* export mode *)
CONST standalone = 0;
CONST linked = 1;
TYPE AccessMode = SHORTINT; (* serial or parallel *)
TYPE ExportMode = SHORTINT; (* standalone or linked *)

CONST serviceName = "RemoteObjects"; TYPE GetParamsProc = PROCEDURE (object: Services.Object; VAR params: PersistentObjects.Object; VAR mode: ExportMode); TYPE CreateProxyProc = PROCEDURE (VAR object: Services.Object; params: PersistentObjects.Object); TYPE RegisterProc = PROCEDURE (object: Services.Object); TYPE Interface = POINTER TO InterfaceRec; TYPE InterfaceRec = RECORD (Objects.ObjectRec) getParams: GetParamsProc; (* may be NIL *) createProxy: CreateProxyProc; register: RegisterProc; (* may be NIL *) msgHandler: Messages.Handler; (* may be NIL *) (* converts messages into operations *) END; TYPE RecoverProc = PROCEDURE (object: Services.Object; errorCode: ErrorCode);

CONST accessFailed = 0; (* access to remote object failed *) CONST invalidMessage = 1; (* invalid message received from partner *) CONST sendFailed = 2; (* cannot send message to remote object *) CONST receiveFailed = 3; (* cannot receive message from remote object *) CONST connectionBroken = 4; (* channel was alive and is now broken *) CONST invalidPartner = 5; (* partner doesn't follow protocol *) CONST unknownObject = 6; (* object not known on the other side *) CONST droppedObject = 7; (* connection to remote object is closed *) CONST closedConnection = 8; (* connection has been closed by partner *) CONST objectWithdrawn = 9; (* remote object has been withdrawn *) CONST listenerKilled = 10; (* channel listener has been shut down *) CONST unexportableObject = 11; (* given object cannot be exported *) CONST unknownType = 12; (* imported object is of unknown type *) CONST unsupportedType = 13; (* ``RemoteObjects'' service not supported *) CONST typeGuardFailure = 14; (* guard of GuardedImport failed *) CONST privateObject = 16; (* object has no public ports *) CONST errors = 16; TYPE ErrorCode = SHORTINT; TYPE ErrorEvent = POINTER TO ErrorEventRec; TYPE ErrorEventRec = RECORD (Events.EventRec) errorcode: ErrorCode; END; VAR error: Events.EventType; VAR errormsg: ARRAY errors OF Events.Message;

(* for RemoteXXX modules *) PROCEDURE Register(type: Services.Type; if: Interface; access: AccessMode); PROCEDURE CreateRegistrationCondition(VAR condition: Conditions.Condition; proxy: Services.Object);

(* client side *) PROCEDURE Import(s: Streams.Stream; VAR object: Services.Object) : BOOLEAN; PROCEDURE GuardedImport(s: Streams.Stream; guard: Services.Type; VAR object: Services.Object) : BOOLEAN;

(* server side *) PROCEDURE Exportable(object: Services.Object) : BOOLEAN; PROCEDURE Export(s: Streams.Stream; object: Services.Object) : BOOLEAN; PROCEDURE Withdraw(object: Services.Object); PROCEDURE AddPort(port: Networks.Socket; address: Networks.Address; errors: RelatedEvents.Object) : BOOLEAN; PROCEDURE RemoveAllPorts; PROCEDURE CloseAllConnections;

(* recovery *) PROCEDURE InstallRecoverHandler(object: Services.Object; recover: RecoverProc); PROCEDURE ReImport(s: Streams.Stream; proxy: Services.Object) : BOOLEAN;


RemoteObjects allows to operate on remote objects as if they would reside locally. Additionally, RemoteObjects supports the operations of PersistentDisciplines. Remote objects are represented locally by proxy objects which forward all operations to the original object.

Delegation to remote objects is possible by converting operations into persistent messages, delivering them over a network connection (see Networks), and by converting them back into operations by a message handler (see Messages). The abstraction-specific conversion of operations into messages and vice versa is done by type-specific modules which offer the ``RemoteObjects'' service for this type (see Services). Note that these service providers are pure add-ons -- the original module which represents the abstraction needs not to be modified or adapted to allow a distributed use of it.

Supporting a type for distribution

Modules which provide a ``RemoteObjects'' service for a specific type have to register themselves during their initialization time by Register which calls Services.Define for them. Note that the service does not only cover type but all extensions thereof if not overridden by more specific service offers (see Services for details).

The access mode determines whether remote calls are to be serialized or not. Note that even in serial mode there may be parallel calls (one from RemoteObjects and at least one locally). Thus, serialization is, if necessary, always in the responsibility of the abstraction. Semaphores (see Semaphores and LocalSemaphores) are recommended for critical regions which may be entered by one party only. The serial access mode, however, is much more efficient than the parallel access mode if all operations have to be serialized anyway. Consequently, the parallel access mode is useful only, if there are no critical regions, or if they are not always entered, or if they are only entered for short time during a longer operation.

The interface if is expected to meet following specification:

getParams: PROCEDURE(object: Services.Object; VAR params: PersistentObjects.Object; VAR mode: ExportMode);
this interface procedure is, if provided, called if an object is exported for the first time. The to be returned persistent parameter object which may be NIL is later passed to the createProxy interface procedure on the importing side. The exporting mode is usually linked. That means that proxy objects are later linked to the original object. In case of standalone, no active connection will be maintained, i.e. objects created later by createProxy cannot delegate operations to the original object. An example for this technique is RemoteIterators which (in some cases) returns in params all informations which are later necessary to perform all operations on the client side. Note that this interface procedure is optional and may therefore be set to NIL. In this case, NIL will be passed later to createProxy.

createProxy: PROCEDURE(VAR object: Services.Object; params: PersistentObjects.Object);
create a proxy object on the client side in dependence of params which has been earlier returned by getParams. Note that params may be NIL. Usually, proxy objects should be an extension of the type which has been passed to Register. This extension should (in case of an linked export mode) implement all operations of the abstraction of type and convert them into messages (see Messages) which are to be sent to itself. RemoteObjects installs a message handler for proxy objects which forwards all messages over the network connection to the original object. Note that RemoteObjects guarantees that the returned message is of the same type as the sent message.

register: PROCEDURE(object: Services.Object);
this interface procedure is, if provided, called if the creation of a proxy object is completed. This allows to invoke some initial operations on the proxy object (e.g. to register itself at the original object). It may be wise to block other operations as long as the registration is not finished by using the condition which is returned by CreateRegistrationCondition. Note that CreateRegistrationCondition is free to return NIL if the registration is already finished.

msgHandler: PROCEDURE(object: Messages.Object; VAR message: Messages.Message);
is a message handler of type Messages.Handler which is installed by RemoteObjects on the exporting side. This handler has to convert incoming messages into operations. Note that the returned message must be of the same type as the incoming message, otherwise the message is considered as unprocessed message. Note that msgHandler is optional and may be specified as NIL.

Exporting objects

An application which is going to export objects, i.e. which is willing to allow other processes to access some of its objects, needs to declare at least one port where other processes may direct their requests to. AddPort adds the given port (which must be a local network address) to the list of ports RemoteObjects listens to. Export makes the given object accessible from outside and writes its address to the given stream which may be later read by Import or GuardedImport. Note that NIL may be passed to Export and therefore later be returned by Import or GuardedImport.

Not all extensions of Services.Object are necessarily exportable because this depends on the existence of an associated service provider. Exportable allows to test whether a specific object can be exported or not.

Note that AddPort and Export lead to the creation of tasks. Because of this, the program does not necessarily finish when all module bodies have terminated. RemoveAllPorts causes RemoteObjects to terminate all tasks which listen for new connections but to keep all existing connections alive. CloseAllConnections requests RemoteObjects to close all connections gratefully (i.e. with notification of the communication partner). To terminate all tasks of RemoteObjects, RemoveAllPorts should be called before CloseAllConnections, otherwise new connections could be opened after closing all old connections.

RemoveAllPorts and CloseAllConnections should usually be called on process termination only and not to close down single services. The export status of an object is better rejected by calling Withdraw. Note that connections are implicitly closed when all proxy objects which use them are dropped (see below).

Importing objects

Import allows to read an object address from the given stream (which has been earlier written by Export) and creates a proxy object for it. Proxy objects delegate all operations (including those of PersistentObjects) to the original object, wait for completion, and return the results.

GuardedImport allows to apply a type guard to the imported object before it gets assigned to object. GuardedImport returns TRUE only if Import would have been successful, and the imported object is an extension of guard, or it is NIL. Thus, GuardedImport works like implicit type tests which are possibly applied for pointer assignments (where NIL is always accepted) but not like type tests or type guards (where NIL would cause them to fail with a runtime error).

RemoteObjects supports Resources for distributed objects:

RemoteObjects guarantees that re-imports return the same proxy object as the first import (as long it has not been dropped). If a party imports objects which have been exported by itself, the original reference is returned. Note that proxy objects may be exported like original objects. In this case, importing parties will delegate their operations to the original object and not to the exported proxy object. Therefore, object identity is preserved independently from the number of involved intermediate parties which have re-exported that object.


By default, proxy objects will terminate if the connection to the original object gets unrecoverably lost. This may be unsatisfactory if it would be possible to re-import the object by other methods (and possibly somewhat later). In this case, InstallRecoverHandler allows to install a recovery procedure recover for a proxy object which will be called in case of broken connections on the client side. Note that multiple or invalid calls of InstallRecoverHandler are silently ignored. The recovery procedure recover is of type RecoverProc and has either to terminate the proxy object (by calling Resources.Notify) or to successfully re-import the given object by using ReImport. This procedure may block the current task for longer time periods and is also free to try re-imports many times.

Proxy objects which are supported by a recovery procedure will switch to the state Resources.communicationStopped when the associated connection breaks. Later, when the recovery procedure returns, the state will switch to Resources.communicationResumed in case of successful recovery and to Resources.terminated otherwise.


Errors and network failures lead to events. All procedures which offer an errors parameter relate all events to this parameter. Messages which cannot be delivered or received due to network failures carry the associated events in the errors component. Operations of PersistentDisciplines relate error events to the proxy object. Following error codes are implemented:
RemoteObjects is unable to establish a connection to the given network address.
The connection has been gratefully closed by the partner.
The communication channel was alive and is now broken.
Cannot send further messages because the object was dropped (i.e. it became unreferenced).
An unexpected message which does not follow the protocol was received.
Either the input from the network connection has been garbled, or the network partner does not follow the protocol of RemoteObjects.
The associated channel listener has been killed by CloseAllConnections or due to program termination (see Process).
The remote object has been withdrawn and is no longer accessible from outside.
RemoteObjects has sent the message to the remote object successfully but didn't receive a response.
RemoteObjects was unable to send the message to the remote object.
Returned by GuardedImport if the to be imported object is neither NIL nor an extension of guard.
There is no ``RemoteObjects'' service provider for the given object and no one could be loaded dynamically (see Services and Loader).
While a valid network address was given to Import, the object is not known on the other side.
The imported object is of an unknown type and it was not possible to load the associated module dynamically (see Loader).
The imported object is of a type which is locally not supported by an associated ``RemoteObjects'' service.
An object could not be imported as it has no public ports, i.e. AddPort was not called by its owner. One possible scenario for this is a process A with public ports that exports objects to process B with no public ports. As there exists already an open channel between A and B, A has no problem to import objects from B as long as they come by request of A (i.e. as parameter of a procedure of A that is called by B). However, B cannot make this object accessible to other processes as they need to establish their own channel to B which fails as B has never opened a port.


generator of RemoteObjects provider modules
abstraction for network addresses and network connections
persistent messages
attachment of persistent auxiliary data structures
error handling
general object states and associated events
implementation of robust proxy objects which take advantage of the recovery mechanism
general mechanism for type-independent extensions which are to be provided by type-dependent providers
callback-mechanism on top of RemoteObjects which allows to forward messages from the original object to the proxy objects
general task management

Edited by: borchert, last change: 2005/08/24, revision: 1.10, converted to HTML: 2005/08/24

Oberon || Library || Module Index || Search Engine || Definition || Module