Oberon ||
Library ||
Module Index ||
Search Engine ||
Definition ||
Module
Ulm's Oberon Library:
RemoteObjects
NAME
RemoteObjects - access of remote objects
SYNOPSIS
(* 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;
DESCRIPTION
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:
- Proxy objects terminate when the original object terminates.
- Exported objects becomes unreferenced only when
no party (either local or remote) uses it.
- Proxy objects which become locally unreferenced
are being dropped,
i.e. they will no longer claim any network resources which
were allocated for them.
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.
Recovery
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.
DIAGNOSTICS
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:
- accessFailed
-
RemoteObjects is unable to establish a connection to
the given network address.
- closedConnection
-
The connection has been gratefully closed by the partner.
- connectionBroken
-
The communication channel was alive and is now broken.
- droppedObject
-
Cannot send further messages because the object was dropped
(i.e. it became unreferenced).
- invalidMessage
-
An unexpected message which does not follow the protocol
was received.
- invalidPartner
-
Either the input from the network connection has been garbled,
or the network partner does not follow the protocol of
RemoteObjects.
- listenerKilled
-
The associated channel listener has been killed
by CloseAllConnections or due to program termination
(see Process).
- objectWithdrawn
-
The remote object has been withdrawn and is no longer accessible
from outside.
- receiveFailed
-
RemoteObjects has sent the message to the remote object successfully
but didn't receive a response.
- sendFailed
-
RemoteObjects was unable to send the message to the remote object.
- typeGuardFailure
-
Returned by GuardedImport if the to be imported object
is neither NIL nor an extension of guard.
- unexportableObject
-
There is no ``RemoteObjects'' service provider for the given
object and no one could be loaded dynamically
(see Services and Loader).
- unknownObject
-
While a valid network address was given to Import,
the object is not known on the other side.
- unknownType
-
The imported object is of an unknown type and it was not
possible to load the associated module dynamically (see Loader).
- unsupportedType
-
The imported object is of a type which is locally not
supported by an associated ``RemoteObjects'' service.
- privateObject
-
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.
SEE ALSO
- genrem
-
generator of RemoteObjects provider modules
- Networks
-
abstraction for network addresses and network connections
- Messages
-
persistent messages
- PersistentDisciplines
-
attachment of persistent auxiliary data structures
- RelatedEvents
-
error handling
- Resources
-
general object states and associated events
- RobustObjects
-
implementation of robust proxy objects which take
advantage of the recovery mechanism
- Services
-
general mechanism for type-independent extensions
which are to be provided by type-dependent providers
- Shadows
-
callback-mechanism on top of RemoteObjects which
allows to forward messages from the original object
to the proxy objects
- Tasks
-
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