Call-out Bracket Methods in Timor
J. Leslie Keedy, Klaus Espenlaub, Christian Heinlein and
Gisela Menger, University of Ulm, Germany
|
 |
ARTICLE

PDF Version |
Abstract
This paper extends the concept of qualifying types by describing how
their implementations can include not only bracket methods which are
applied when a method of a target object is invoked, but also further "call-out" bracket
methods which can be applied to invocations by the target object of
the methods of other objects. This additional technique can be used
for example to provide enhanced synchronisation in qualifying types,
as an aid to confining the activities of an object, and as a means
of providing parallel activities associated with the sending and receiving
of information, e.g. encryption and decryption, data compression.
1 INTRODUCTION
In an earlier paper we have described the concept of qualifying types,
i.e. types whose objects (known as "qualifiers") can dynamically
qualify the behaviour of objects of other types (known as "targets")
by means of bracket methods [5]. As presented in that and earlier papers
[2-4], qualifying types permit modules to be written in the programming
language Timor1 which can, for example, provide such general services
as synchronisation, protection and logging. The bracket methods which
carry out such activities are applied to the incoming invocations of
a target object's methods. A technique for statically incorporating
qualifying types into other types was presented in [6].
The present paper extends the concept of qualifying types by describing
call-out
bracket methods as a new category of bracket methods. These function
in a similar way to those of normal bracket methods with
the difference that they are applied to the outgoing calls which an
object
might make to the methods of other objects. Call-out brackets can
for example be used to release the semaphores acquired on entry to
an object
when this calls the methods of further objects. They can also be
used to implement information confinement policies for objects or to
journalise
communications with other objects.
The bracketing of outgoing calls from a source object might also
be coupled with a complementary bracketing of incoming calls to
a target
object to provide services which require parallel activities when
passing and receiving information, such as encryption and decryption,
data
compression and expansion, etc.
It is assumed that readers are familiar with the basic idea of
qualifiers [5, 6], so that we do not here repeat information
about the structure
of Timor nor how qualifiers work in general. In the current paper
we refer to the bracket methods described in the earlier papers
as call-in
bracket methods.
2 CALL-OUT METHODS: AN OVERVIEW
As a first illustration of the idea behind call-out methods we define
a type whose instances are able to qualify target objects such
that whenever an instance method of the target is invoked mutual exclusion
is guaranteed and whenever the target invokes any other object
the
mutual exclusion is released, but this is then claimed again as
the outgoing call returns to the method of the target. Here is a type
definition:
and now an implementation:

As this is not a paper on synchronisation we
refrain from a discussion of the usefulness of such a module, except
to point out the obvious: that a thread which uses such a qualifier
has no guarantee that the state of the target will be the same before
and after it calls out to another object.
From the viewpoint of the design of the Timor language we see that
call-out methods are listed in a callout clause, which is similar
to the qualifies clause for call-in bracket methods (as described in
[5]).
In the above example all calls out of the target to any other object
are handled by the same call-out code.
The definitions of the bracket methods for incoming and outgoing
calls have the same syntax. The difference in their meaning is
illustrated in Figure 1:

Call-in bracket methods "catch" a client's method invocation
to the target (qualified) object, and begin executing their own code
as defined in the qualifies section. If the call-in method executes
a body statement the target method is then invoked. If the target itself
then calls some other object (the "call-out" object), the
qualifier's call-out bracket method(s) (defined in a callout section) "catch" this
invocation on the way out. If the call-out bracket method then executes
a call statement the call-out object is then called (or the next call-out
bracket is activated). After the called object returns the call-out
method continues execution at the statement following the call statement,
and when this exits a return is made to the target object, or to the
call-out bracket from which it was invoked.
A client object and a target object can of course both be qualified
(usually by different qualifying objects, although we will see later
that it sometimes makes sense for the same qualifier to qualify both).
If a client object has a qualifier with call-out methods these methods
are executed before the call-in methods of a qualifier associated
with the target (cf. Figure 2):

3 DEFINING CALL-OUTS FROM OBJECTS
Like call-in qualification, call-out qualification can be defined
to apply either to any interface or to a specific view or type interface.
However, whereas the counterpart of the bracket methods for call-in
qualification are the methods of the object with which the qualifier
is associated, the counterpart of the bracket methods for call-out
qualification are the methods of other objects which it invokes.
Since the type of an object is not normally the same as that of
the
objects which it invokes, there is no implied type relationship
between call-in and call-out types. Thus for example call-ins might
be applied
to a specific view or type while the call-outs might refer to any
type, or a qualifying type might define only call-ins or call-outs.
An Example of Standard Call-ins and Call-outs: Monitoring
Following a callout any clause only standard call-out methods,
i.e. methods defined to qualify all methods of the called object,
or more
specifically its op (writer) or enq (reader) methods, can be
defined. Such qualification can be useful not only to support synchronisation
(as illustrated in section 2) but also for example to monitor
the
activity of a target object, e.g. by maintaining counts of
calls:

with an implementation:

An Example of Standard Call-outs: Information
Confinement
A qualifying type need not define call-in bracket methods. To illustrate
how call-out methods alone can be usefully defined we consider how
simple information confinement policies might be enforced, beginning
with a type Confined which ensures that its target object does not
make any calls whatsoever to other objects (and therefore cannot
release information, as Timor strictly enforces the information-hiding
principle
[8]):
Although this type would be useful in many circumstances,
it would often be desirable to enforce confinement policies which allow
specific calls to be made. In this case the type Confined can be expanded,
using a variant of normal inheritance which allows bracket methods
to be inherited. In Timor the programmer has a choice between subtype
polymorphic type inheritance (using the keyword extends) and interface
inheritance without polymorphic implications (keyword includes). Here
we choose the latter to define a qualifying type which only allows
its target to make calls to the print method of a specific printer
object. First we define a view which includes the permitted print method,
then the qualifying type:

This type contains two callout sections, one
of which is inherited from Confined. The new callout section, which
is more specific and therefore has priority, defines what happens if
an object containing the view Printer is called by the target. In this
case the standard call-out bracket method (for all) is effective if
the more specific method print has not been invoked. The overall effect
is that only print calls to the correct printer are permitted. The
inherited callout section (for any) defines what happens if objects
of any (other) type are called, i.e. the object cannot make calls to
objects other than the printer. Here is an implementation:

The built in expression calledObject returns
a value of type ObjId, which is a special type whose values are unique
object identifiers. It can be used in both call-in and call-out bracket
methods to determine which object is being called. Timor supports several
such pseudo-identifiers which allow a bracket method to determine the
environment in which it is working. However, the protection mechanisms
provided by Timor are not the subject of this paper, and are therefore
not described here in detail.
The effect of this qualifier is that the only call-out which a target
object can make is a print call to a defined printer. Hence it is
confined to using this printer. With call-out qualifiers in Timor any
confinement
policy known to us, including for example the Bell-LaPadula model
[1], can be straightforwardly implemented.
Combining Call-in and Call-out Brackets: Communication Services
As we have seen in the examples of synchronisation (section 2) and
monitoring (section 3) it is sometimes useful for a qualifying
type to include call-in and call-out brackets which are designed
to be
applied to the same target object (cf. Figure 1). However, it
can be equally
useful in some cases to define a qualifying type which has call-out
designed to operate in one target and call-in brackets designed
to operate in another. Examples of this kind of communication
service include compression or encryption of data in a call-out bracket
of
a sender object complemented by decompression or decryption in
the call-in of the same call in a receiver object, cf. Figure
3.
To
illustrate this we define that text can be transmitted as a parameter
to a method transmit, which is defined in a view that can be incorporated
into many types.

Here is a skeletal implementation:

In the example it is assumed that the same
encrypting object is used to bracket both the sender and the receiver.
If the call is a local method call this could be useful, for example
to hide the content of the message from later call-out and call-in
brackets which might potentially contain trojan horses. In the case
of a remote procedure call in a conventional environment, the desired
effect could be achieved by having separate instances of the qualifier
at the different locations, each initialised with the same keys and
using the same algorithm. A symmetrical encryption algorithm is assumed,
but alternative encryption qualifying types could be developed which
use asymmetrical encryption algorithms.
4 USING CALL-IN AND CALL-OUT BRACKETS DYNAMICALLY
Qualifying types can either be associated with individual target
objects dynamically, by placing them in a List<:Qualifier*:> [5]
or bracketing can be defined statically in type definitions [6]. In
this section
we consider how the dynamic case is affected by call-out brackets.
Using Both Call-in and Call-out Brackets on a Target
In the normal case all the bracket methods (for call-in and for call-out)
of a qualifier which appears in a List<:Qualifier*:> are applied
to the target as appropriate. Thus an object can be instantiated as
follows if it is to be exclusively synchronised when its methods are
called and the synchronisation released when it makes nested calls
(cf. section 2):

Using Call-out Brackets to Implement Pipes
In some cases qualifiers may only have call-out brackets, for example
to transform the output of an object for use as the input of another
object in a manner resembling Unix pipes. Suppose for example that
the method printOutput of objects of type Atype sends a text file
to a specific printer by invoking the latter's print method (described
earlier in the view Printer). If the user wants to reorganise the
output, e.g. by first sorting it, then truncating the first 50 lines,
he might use two call-out qualifiers (of types Sorter and Truncater),
which in their call-out brackets for print manipulate its Textfile parameter as appropriate. To set up the "pipe" he could
use the following code:

He could then create an object of type Atype,
which is qualified by these qualifiers:

As will be explained in section 6, the order
in which call-out brackets are executed is the reverse of their order
in the qualifier
list. To print the sorted and truncated text file he then simply invokes
the printOutput method of myObject:

This example uses a list literal to associate the qualifiers
with the object to be qualified. This is appropriate if myObject is used once
only. However, if myObject is intended for frequent use with different
qualifiers in a "pipe" then it could be set up as follows:
Subsequently myList could then be changed by
inserting new qualifiers and removing existing ones, as described in
[5].
The advantage of using call-out methods in conjunction with myObject,
rather than similar call-in brackets of the Printer object invoked,
is that each user can trim his own output as he wishes, without affecting
other users of the Printer object.
Rules for Selecting Call-in and/or Call-out Brackets on an Individual
Basis
As we saw in section 3, the call-in and call-out brackets of the
same qualifier may need to be associated with different targets (e.g.
a
sender and a receiver) and it must therefore be clear what bracket
methods should be applied to a target, the call-in methods, the call-out
methods, or both. The technique adopted to achieve this in Timor is
based on the idea that a reference for a qualifying type (i.e. a subtype
of the type Qualifier) has two boolean values associated with it indicating
whether the call-in and call-out brackets of the referenced qualifier
are "active". This is the "normal" state of these
indicators when a reference is declared and initialised to null, but
bracket methods can be deactivated by using the arrow operator -> in
a reference expression. If the arrow appears before the reference expression
then the call-out brackets are deactivated (and the call-in brackets
remain active), while its use following the expression deactivates
the call-in brackets (and indicates that the call-out brackets are
active). The arrow operator can only be used once in a reference expression.
It is an error, detected at compile-time, if the arrow operator is
used with any reference which is not for a qualifier, or for a qualifier
reference which does not have the kind of brackets which are to remain
activated. Once deactivated for a reference, bracket methods cannot
be reactivated in association with the currently bound qualifier.
Assignment statements and parameter initialisations which have a
reference with deactivated bracket category in the source reference
result in
the same restriction being set on the target reference, i.e. a restriction
is not a permanent property of a reference as such, but only in so
far as it is bound to a particular qualifier. Thus if an assignment
statement has on the left side a reference with deactivated call-out
brackets and the reference being assigned has deactivated call-in brackets,
the result is that the reference on the left side becomes identical
with that on the right side, without a run-time error occurring.
It is not considered to be an error to deactivate a bracket method
category which has already been deactivated.
With this background, the Timor rules for dynamically selecting call
brackets can now be formulated as follows:
a) If a qualifier has both call-in and call-out brackets, but only
its call-in bracket methods are to be applied to a particular target,
the programmer deactivates the call-out brackets by prefixing the qualifier's
reference with the operator ->.
b) If a qualifier has both call-in and call-out brackets, but only
its call-out bracket methods are to be applied to a particular target,
the programmer deactivates the call-in brackets by placing the operator
-> after the qualifier's reference.
c) If the operator -> is not used, all the bracket methods (call-in
and/or call-out) associated with a qualifier in a List<:Qualifier*:> are
applicable to a target.
To heighten the clarity of programs it is recommended that bracket
methods are deactivated as they are inserted into qualifier lists.
Using the Rules for Communication Services
A user wishing to send an encrypted message (cf. section 3) could
set up the sender and receiver objects as follows:
where the type Sender is defined as follows:

with an implementation such as:

and the type Receiver includes the view Transmission.
Using the Rules to Mask Out Call-in or Call-out Brackets
The above rules can be used not only to control communication services.
They can also be used simply to mask out either the call-in or
call-out brackets
of a qualifier. For example a user wishing to monitor only the calls from
an object using CallCounting (cf. section 3) might proceed as follows:

The arrow operator can also be used in a parameter
to the insert method of List, but only in the context of insertion
into a qualifier list, e.g.

5 STATIC TYPE DEFINITIONS WITH CALL-OUT BRACKETS
The syntax for declaring qualifiers statically for a new type was
described in [6], a knowledge of which is assumed in this section.
In the normal
case, if a qualifier which has call-out brackets in its type definition
appears in a static type declaration the call-out brackets are
applied in an analogous way to their dynamic use. In this section the
various
possibilities, including restricting the use of call-in or call-out
brackets are illustrated.
Static Call-in and Call-out Brackets for
the Same Target To define a Thing which is statically bracketed by the call-in
and call-out brackets of ReleasableMutex is trivial:

Static Call-out Brackets Users can feel more confident that their information is secure if
a print spooler is available which has been statically confined
to accessing only a specified
printer. For this purpose we assume the existence of a type Spooler,
which extends the Printer view (i.e. has the method print as defined
in section
3). In a new type ConfinedSpooler this could be statically confined
by an instance
of the type PrinterConfined (also defined in section 3) as follows:

This might be implemented along the following lines:

Masking out Brackets
Masking out either the call-in or call-out brackets statically
(in the following example to apply only call-out brackets)
can be achieved as follows (using a static version of the example
in section 4):

Here is an implementation:

In the case of static declarations the arrow operator is associated
(as a prefix for call-in brackets, as a suffix for call-out brackets)
with the type name of the qualifier in both type definitions and their implementations. This
can only occur in a qualifyingList (see the EBNF syntax definition
in section 3
of [6]). A compile time check is carried out to ensure that the type in question
has bracket methods of the type indicated.
Notice that the use of an arrow in association with a type name
is not to be understood as a property of the type being declared,
but indicates merely
that
any qualifier being assigned (in an implementation) to the static qualifier
will automatically have the corresponding bracket category deactivated
(if it is not already deactivated, in the case of a reference).
As references for external qualifiers can be passed into the
maker of a statically qualified type [6], the arrow operator can
also be associated
with a type
name (with the same meaning) in the parameter list of such a maker, but
only where
the type definition contains a qualifyingList with an entry for the same
type and with the same use of the operator. This rather unusual rule
has two advantages.
First, it allows the invoker of a maker to see the restriction without
having to examine the code of an implementation. Second, it allows the
condition
to be checked at compile time (at the point in the client code where
the maker
is invoked).
Communication Services
We now define an encrypting communication service in which sender
and receiver are statically bracketed. In this example we confine
the sender to using the Transmission view (assuming that a type
TransmissionConfined has been defined by analogy with PrinterConfined in section 3) and the receiver to printing out the message to
a defined printer). Whereas the confinement bracketing can be
defined "by value", the encryption is defined by reference
cf. [6], allowing the sender and receiver to share the same qualifier.
We begin with the sender:
The receiver definitions are as follows:

Other cases, such as the simulation of
Unix pipes, present no problems in static definitions, although
to define pipes statically would defeat their purpose from the
viewpoint of flexibility.
6 SCHEDULING MULTIPLE QUALIFIERS
As was discussed in detail in [5], call-in bracket methods in
a qualifier list are applied to the target object from left
to right,
i.e. the first qualifier in a qualifier list (or in the static
case in a qualifyingList [6]) is applied, if it has a matching
bracket method, then the second qualifier in the list, etc.
In contrast, call-out qualifiers are applied in the reverse order
of the list, i.e. from right to left. This has the effect that
when a qualifier contains both kinds of bracket methods, these
are nested as illustrated in Figure 4:
In
this example the first entry in the qualifier list is the qualifying
object 1, followed by the qualifying object 2.
7 RELATED WORK
The relation between qualifying types with call-in bracket methods
and other work was discussed in detail in [5, 6]. Here it remains
for us to discuss work related to call-out brackets. And here we
have the problem that no such work is known to us.
What can be said however, is that there is clearly a close relationship
between a call-out bracket associated with a client and a call-in
bracket associated with its target. Thus it might appear that some
of the examples in this paper can be handled by techniques which
we have previously compared with call-in brackets. For example,
combining call-in and call-out brackets for a single target (cf.
ReleasableMutex in section 2) can be simulated using aspect oriented
programming (AOP) techniques (cf. [7]). This is possible because
AOP in effect allows program text to be cut and pasted into a class.
Hence it can be effected at arbitrary points in a text, either
where an object starts executing (cf. call-in) or where it calls
another object (cf. call-out). But call-out brackets associated
with one object (e.g. a sender) and call-in brackets associated
with another object (e.g. a receiver) cannot be provided as a single
AOP "module" in the way that this is possible in Timor.
In addition, the other comments made in our comparison with AOP
in [5] still apply, e.g. regarding the restrictions arising from
(a) not distinguishing between op and enq methods, (b) operating
at the source or bytecode level and thus affecting all objects
in a class, (c) not being able to associate a qualifier with a
group of objects, (d) not treating qualifier methods as independent
methods but as new methods of the class, etc.
A significant advantage of call-out methods is that they can
be different for different callers of the same destination object.
This is important for example in simulating Unix pipes. On the
other hand it is sometimes desirable, as when confining a printer
spooler, to ensure that the same call-out bracket methods (which
guarantee the confinement) are statically defined and cannot
be
changed by different users.
8 CONCLUSION
The call-out bracket technique extends the notion of qualifying
types as described in earlier papers (e.g. [4-6]) to allow
the calls out of a target module to be bracketed in a similar
manner
to the calls into the target. This considerably extends the
usability of qualifying types, for example by allowing more
complex synchronisation
and monitoring policies to be defined and implemented. It
also opens new possibilities in the area of computer security,
particularly
in providing a technique which allows such protection features
as information confinement policies and modular encryption
techniques to be implemented at the programming language
level2. It also
introduces the possibility of providing a flexible Unix-like
pipe mechanism
at the programming language level.
ACKNOWLEDGEMENTS
Special thanks are due to Dr. Mark Evered and Dr. Axel Schmolitzky
for their invaluable contributions to discussions of
Timor and to the ideas which have been taken over from earlier
projects. Without their ideas and comments Timor would
not have been
possible.
Footnotes 1 www.timor-programming.org
2 This parallels our work in the
operating system area, where the SPEEDOS operating system (cf. www.speedos-security.org)
supports the notion of bracket methods for call-ins and call-outs at
the system level.
REFERENCES [1] D. E. Bell and L. J. LaPadula, "Secure
Computer Systems: Mathematical Foundations," Mitre Corp., Bedford,
Ma. ESD-TR-73-278, 1973.
[2] J. L. Keedy, M. Evered, A. Schmolitzky, and G. Menger, "Attribute
Types and Bracket Implementations," 25th International Conference
on Technology of Object-Oriented Languages and Systems, Melbourne,
1997, pp. 325-338.
[3] J. L. Keedy, K. Espenlaub, G. Menger, A. Schmolitzky, and M.
Evered, "Software
Reuse in an Object Oriented Framework: Distinguishing Types from Implementations
and Objects from Attributes," 6th International Conference on
Software Reuse, Vienna, 2000, pp. 420-435.
[4] J. L. Keedy, G. Menger, C. Heinlein, and F. Henskens, "Qualifying
Types Illustrated by Synchronisation Examples," in Objects,
Components, Architectures, Services and Applications for a Networked
World, International
Conference NetObjectDays, NODe 2002, Erfurt, Germany, vol. LNCS 2591,
M. Aksit, M. Mezini, and R. Unland, Eds.: Springer, 2003, pp. 330-344,
http://link.springer.de/link/service/series/0558/papers/2591/25910330.pdf.
[5] J. L. Keedy, K. Espenlaub, G. Menger, and C. Heinlein, "Qualifying
Types with Bracket Methods in Timor," Journal of Object Technology,
vol. 3, no. 1, pp. 101-121, http://www.jot.fm/issues/issue_2004_01/article1,
2004.
[6] J. L. Keedy, K. Espenlaub, G. Menger, C. Heinlein, and M. Evered, "Statically
Qualified Types in Timor," Journal of Object Technology, vol.
4, no. 7, pp. 115-137 http://www.jot.fm/issues/issue_2005_9/article5,
2005.
[7] G. Kiczales, E. Hilsdale, J. Hugonin, M. Kersten, J. Palm, and
W. G. Griswold, "An Overview of AspectJ," ECOOP 2001 - Object-Oriented
Programming, 2001, Springer Verlag, LNCS, vol. 2072, pp. 327-353.
[8] D. L. Parnas, "On the Criteria To Be Used in Decomposing Systems
into Modules," Communications of the ACM, vol. 15, no. 12, pp.
1053-1058, 1972. About the authors

|
|
J. Leslie Keedy recently retired
from the position of Professor and Head, Department of Computer
Structures, University of Ulm, Germany, where he lead the Timor
language design and the Speedos operating system design groups.
His email address is keedy@jlkeedy.net. His biography can be
visited at http://www.jlkeedy.net/biography_short.php
|
 |
|
Klaus Espenlaub completed
his Ph.D. in Computer Science at the University of Ulm in 2005.
Currently
he works as a research assistant in the Department of Computer
Structures at the University of Ulm. His research interests include
secure operating systems, protection mechanisms and computer
architecture. His email address is espenlaub@informatik.uni-ulm.de.
|

|
|
Christian Heinlein received a Ph.D.
in Computer Science from the University of Ulm in 2000. Currently,
he works as a scientific assistant in the Department of Computer
Structures at the University of Ulm. His research interests include
programming language design in general, especially genericity,
extensibility and non-standard type systems. His email address
is heinlein@informatik.uni-ulm.de. |

|
 |
Gisela Menger received a Ph.D. in
Computer Science from the University of Ulm in 2000. Currently
she works as a scientific assistant in the Department of Computer
Structures at the University of Ulm. Her research interests include
programming language design and software engineering. Her email
address is menger@informatik.uni-ulm.de.
|
Cite this column as follows: J.L. Keedy, K. Espenlaub, C. Heinlein,
G. Menger: “Call-out Bracket Methods in Timor”, in Journal
of Object Technology, vol. 5, no. 1, Januar - Februar 2006, pp. 51-67, http://www.jot.fm/issues/issue_2006_01/article1
|