A Message Bus is a generic denomination to refer to a sytem that provides an easy way to allow communications between heterogeneous (i.e. not necessarily running on the same machine) applications that don't need to know about each other. Specifically, each application sends messages with a tag on them without knowing who will be answering its queries. Interested clients merely express interest in a set of tags somehow (1) and then listen to incoming messages, handling them as they arrive.
Koalatalk is the successor to the Koalabus ([1] [2]). For an overview of the Message Bus history, see [1]. It has been heavily inspired by SunSoft's Tooltalk ([3] [4]) for reasons that will be explained in the next section.
Tooltalk is emerging as the leading standard, mostly due to the fact that it has been chosen for both CDE (Common Desktop Environment, <add reference here>) and COSE (Common Open Software Environment, <add reference here>).
Tooltalk introduces the notion of observers and handlers which receive messages of two types : notice messages (annoucing events) and request messages (asking operations to be performed and waiting for the results). Message addressing is very flexible : a message can be sent to a specific process, a particular type of process or a process that can perform a particular operation on an object. Messages can be object-oriented and include persistent storage and automatic activation of applications to perform object operations.
Despite these many interesting functionalities, many are cumbersome to use and sometimes half-implemented (the object part of Tooltalk is not at all compliant with the COSE specifications, and an object-addressed message is nothing more than a superset of a process-addressed message).
Thus, we felt a need to develop another Message Bus system that would meet the following criteria :
This last point showed up as the most important after a poll of KoalaBus users. A direct connection (that would bypass the server) was the most blatant lack of the KoalaBus, and of Software Buses in general. Users often found themselves having two processes communicating with each other but needing the server as an intermediary to do so.
From an architectural point of view, this direct connection may seem to be a hole in the "hiding clients" philosophy that is the starting point of the Message Bus technology. To emphasize the difference between a "regular" Message Bus (Tooltalk) and Koalatalk, we can define the following two categories :
We felt that this "point-to-point" feature was
sometimes really needed and couldn't be emulated with the existing
"central message passer" method (3). This accounts for
the decision to give ktclients the ability to communicate with each other
directly, either explicitly (kt_MessageSendToPeer()) or transparently
(kt_MessageSendWithReply()), and in that latter case, Koalatalk
will locate the "right" recipient for you.
The following features from the Koalabus were left out in Koalatalk :
The following Tooltalk features are present in Koalatalk :
However, most of these notions have been simplified in the process (see section Concepts).
The following Tooltalk features have not been implemented in Koalatalk :
The following section explains in details how Koalatalk works. You might want to skip directly to the section Quick start for a quick overview.
Koalatalk can be compiled on any architecture running X11R6. It won't work on X11R5 and below (5).
"Koalatalk" refers to a set of two distinct components :
libkoalatalk.a. A library which every client must link to
in order to use the Koalatalk API;
A message needs to be "tagged" in order for the Koalabus to be able to locate which recipients are eligible to receive it. Thus, each message must be accompanied with a textual string called a Koalatalk Resource Locator (KRL) (8).
On the recipient side, you have to declare a regular expresion that will be compared against the KRL's Koalatalk receives. This regular expression is referred to as a Koalatalk Resource Specification (KRS). When Koalatalk receives a message destined to a certain KRL, it matches it with all the KRS'es it knows and if a match occurs, the ktclient that declared this KRS will receive the message.
For example, a compiler might send a notice each time a compilation
error is found. This notice could be sent to KRL "compiler:error". If a
text editor registers its interest as an Observer in the KRS
"compiler:error.*" (this is a regular expression), then it will
receive all the notices emitted by the compiler.
A KRS can be seen as a "class of KRL's" : it contains all the KRL's that will be positively regexp-compared with it.
Messages are split in two categories : Notices and Requests.
For example, a debugger might send a notice each time the user sets a breakpoint. This information might interest a text editor which is currently editing the source so that it highlights the line where the breakpoint is.
When you send a notice with kt_MessageSend(), it is sent to
`ktserv' (which must be running) which then decides who it should
be forwarded to (see below for a thorough explanation of how this
resolution is performed).
Notices are sent to Observers only (i.e. who used kt_KRObserve()
to register their interest).
Actually, each ktclient has its own list of available handlers,
which is kept up to date by `ktserv'. Each time a new Handler
declares itself (with kt_KRHandle()), the server forwards this
information to each connected client, along with the coordinates of the
handler. This way, each ktclient can find the corresponding
Handler without the help of `ktserv' and open a direct connection
with it. This same connection will be reused if more information is
exchanged between the two parts later on. All this work is transparent
to the caller.
To sum up : ktclient caches some of ktserv's knowledge. Not all of it, only Handlers, since a direct connection will most likely be needed in such situations, unlike notices which can usually afford a slight overhead due to their "informative-only" nature.
If ever a request is issued and ktclient knows no handler that could handle it, several cases may arise depending on the request's mode
You can register your interest in a KRS either as an Observer or as a Handler. Observers receive Notices, Handlers receive Requests.
Nothing is expected from an Obserer : the message is merely transmitted to it by `ktserv'. However, a Handler is a bit different in two respects :
Thus, an observer implements a "procedure", or an asynchronous RPC call
Messages exchanged between ktclients have the following contents :
Note that no suppositions are made about what the message contains. If the ktclient decides to use the operation field to contain the date of the day or whatever, it is free to do so. Koalatalk won't enforce any rule on the contents, it acts merely as a message passer.
The term "Message" might be a bit misleading since it often refers to a pair "Message / KRL". When you "send a message", you actually "send a message to a KRL". Keep that point in mind.
Sometimes, you know you may need a ktclient at one point but don't want it to be permanently connected to Koalatalk. Thus, you would like `ktserv' to launch automatically a specific program if it receives a specific message, and possibly have this ktclient quit once it has performed its task.
To achieve this, you must supply some information to `ktserv'. This information is referred to as the "protocols `ktserv' knows". The term protocols has been borrowed to the Objective C terminology ([5] [6]).
In Objective C, a Protocol is a set of certain methods an object is supposed to respond to. Sometimes, when you design an object with multiple inheritance, you usually don't want all the features of A and B, but only features X from A, and Y from B. This can be encapsulated in a Protocol.
Thus, a Protocol can be seen as a subset of messages a certain object responds to. This definition applies perfectly to Koalatalk : a ktclient needs only tell `ktserv' some of the messages it can handle/observe. If such a message reaches `ktserv', it forks the specified ktclient and forwards the message to it. This feature is very close to the "autoloading" of functions (for interpreted languages, Lips, TCL, etc...), or "dynamic loading" of libraries in compiled languages (C, C++, etc...).
Strictly speaking, the ktclient might just declare for its
protocol a single message "launch_me.*" and then declare more
KRS'es once it is launched. But the current implementation will let you
specify an indefinite number of KRS'es in your protocol file.
A protocol file corresponds to declaring one or several KRS'es, whether they are observed or handled and an executable to fork. Just like the normal dispatching mechanism, these KRS'es will be matched against the incoming KRL of the message.
Note that the forked ktclient will still have to register a KRS matching that of the message that triggered its launching before it actually receives the message. See section Quick start, for a practical demonstration of Koalatalk usage.
See section ktserv, for a detailed explanation on how `ktserv' reads the protocols.
This section is destined to people who compiled Koalatalk and wish to see quickly what can be done with it.
First thing to do is launch a server (13).
$ ktserv ktserv starting pid 10558 kts_initServer: listening to 2 descriptors Server starting on port 3661
Depending on the installation, you might see a warning from the server saying something like
*** ktserver warning : couldn't find protocol file 'protocols'
Ignore it for the moment. It is good practice to immediately set the
variable $KTSERV_HOST to the host on which you just launched
ktserv. Suppose `ktserv' is running on host "indri" and you want to
launch ktmon on "arthur", you have to do
$ KTSERV_HOST=indri
for ksh and likes, or
$ set KTSERV_HOST indri
for csh and likes.
Now let's launch our first Koalatalk client, ktmon.
$ ktmon&
Two windows will appear, one is called MessagesW and the other one
is MainW.
Depending on your `app-defaults' file, you might have different
default settings, so I will assume the minimum here. Logically, you will
have set the -autoConnect option, either via resource
(Ktmon.autoConnect: True) or with the command line (ktmon
-autoConnect) (see section Utilities for more details on the syntax).
In MessagesW, set the following :
Sendto... : KRL Destination : ko Operation : <no op> Message content : 1st argument, 2nd argument and 3rd argument Notice : on
then click on the Send message push button. As soon as you do it,
a message will appear in the Log (in MainW), saying something like
>>> Sent a notice to KRL:ko
Note that the symbol >>> indicates a message was sent, and
<<< will mean a message was received. Apart from this message,
nothing much happened, except that the server itself reacted to this
message. You just sent a message to the server but since it found
nobody interested in it, `ktserv' didn't forward it and threw
it away.
Now let's configure our ktmon so that it receives its own
message. In MainW, set
KR Spec : k.*
then click on the Add button (or press Return after
typing k.*). The little list on the right of the TextField
will show something like
k.* (101 o)
The 'o' means "observing" (other possible value : 'h', for
"handling"). The number 101 is the ID that was given to you,
Now, click on Send message in MessagesW again.
The Log now shows more information :
>>> Sent a notice to KRL:ko
<<< Observed a message, number of arguments : 3
<<< Matching id 101
Operation : <no op>
1st argument
2nd argument
3rd argument
After sending the notice, it immediately received it (as indicated by
the <<< symbols). It says the received message matches the
KR Spec registered under the number 101, and gives the content of
this message.
You might want to launch another ktmon and make sure the messages
are transmitted from one to the other if you set them properly (i.e. one
observes the right KRS and the other one sends a message with the
proper KRL, just like we just did. Don't worry if these names are
unfamiliar right now, you don't need to understand them at this point).
Launch another ktmon and when you are convinced it is actually
working, let's try direct communication.
Each Koalatalk client has a unique identity number. ktmon
indicates it in the upper-left corner of MainW. It will be something
like
tcp/indri:3825
This identity number may be used to open a direct communication between
two `ktserv' client (but
we'll see a better way to achieve that shortly). So now you have two
ktmon running. Let's call the first one A and the second
one B. Write down A's identity and now in B set
Send to... : Peer
Destination : A's identity
So B is sending a direct message to A. As expected,
B's log says
>>> Sent message to peer
and A's log says
<<< Received direct message from a peer
<no op>
arg1 : 1st argument
Notice the word "direct". As you can see, when a ktclient receives a message, it can tell if it was sent directly to it by a peer, or if it was forwarded by `ktserv'.
Now let's try to illustrate what a handler is. Quit both
ktmon's and launch one again. For clarity's sake, the following
examples will be shown on one ktmon only, but you might
just as well use two.
A request is a message you send to "someone", and expect an answer. It might be a task you want to be performed, an information you want to retrieve, etc... Let's send a request first :
Send to... : KRL Destination : koala Operation : test of a request Request : on
and click on Send message. The log faithfully acknowledges
that the request has been sent, but nothing more should happen
(14). This is
normal, you are supposedly the only ktclient connected.
Next step is to create someone who will handle this request so that
we get a response. In order to do that, set the MainW with the
following values
KR Spec : k.* Handle : on
then click on Add and make sure the pattern has been registered
with the flag 'h'. As soon as you do that, the log will echo
<<< Handled a message
test of a request
and a new window will pop up. Since no handler was present when you issued your request, Koalatalk spooled it, waiting for a handler to appear. When you handled the pattern, Koalatalk recognized you as a handler for this pending request and sent it to you.
ktmon handles this by asking you what answer you wish to
reply. Just fill the message as you wish (fill the three Arguments slots)
and click on Send. The log confirms the reply to the request
was received :
<<< Received direct message from a peer
arg1 : reply
(the blank line simply indicates that the reply contains a blank Operation field. Of course, arg1 will have a different value depending on what you typed in the TextField widget...).
Sometimes, you will want to send a request but no handler is connected to the server. In such a situation, you might want to have `ktserv' start automatically an application that can handle your request and perform the desired operation.
In order to achieve this, `ktserv' reads Protocol files when it is started (15)
We'll configure `ktserv' so that if ever a request addressed to
KRL mon is sent, then it launches another
instance of ktmon. The simplest way to achieve this is to edit a
file called `protocol' in the same directory as `ktserv', and
to put in it the following lines :
@
/u/indri/0/koala/beust/ice/koalatalk/sun4/ktmon/ktmon
handle {
mon.*
}
Of course, you might have to modify the absolute path to reflect the
location of your ktmon. Next, we must tell `ktserv' that its
protocol files have been changed. To do that, find the process id of
`ktserv' (16) and send the following signal (USR1):
kill -USR1 <ktserv id>
Then `ktserv' will display something like
Rereading protocol files
You can ask `ktserv' what protocols are currently know with the
USR2 signal :
kill -USR2 <ktserv pid>
which causes `ktserv' to display
Known protocols : -------------------------------------------------- /u/indri/0/koala/beust/ice/koalatalk/sun4/ktmon/ktmon handling 'mon.*'
Now that `ktserv' knows how to create the right KRL, fill
MessagesW as follows :
Send to... : KRL Destination : mon Operation : <no op> Request : on
Shortly afterwards, another ktmon should pop on your screen. But
we are only halfway : whatever happened to the request? The new
ktmon didn't receive it, so how is it supposed to perform the
task we expect from it?
All you have to do is ask the new ktmon to declare itself as a
handler with the following settings in MainW :
KR Spec : mon.* Handle : on
and suddenly, the request will be forwarded to it and you can reply to it, just like you did a few minutes ago.
The distribution comes with a program called handletime in the
`src/handletime/' directory. Add the following protocol to
`ktserv' :
@
/u/indri/0/koala/beust/ice/koalatalk/sun4/handletime/handletime
handle {
get_time.*
}
(don't forget to modify the path accordingly). This program handles
requests addressed to the KRL get_time and will reply by
setting the first argument to the current date. To try it, set
MessagesW with the following values :
Sent to... : KRL Destination : get_time Operation : <no_op> Request : on
and Send it. Within the time taken by `ktserv' to launch
`handletime', you should receive the following reply :
>>> Sent a request to KRL:get_time
<<< Received a reply to request
time
arg : Tue Aug 16 15:06:40 1994
Use respectively kt_Init() and kt_UnInit() to start and
end a Koalatalk session :
Kt_Context context = kt_Init(0, 0, 0, 0); ... kt_UnInit(context);
kt_Init() will initialize the "direct" file descriptor. As soon
as kt_Init() is finished, any Koalatalk client can send you a
direct message (17). If you are interested in direct messages
(you can choose to ignore them), use kt_PeerAddMsgCallback()
with the appropriate callback (see below).
The parameters given to kt_Init() are used when you wish
to perform multiplexed input on several file descriptors
(see section API reference and section Frequently Asked Questions section
about this).
kt_LocateServer(). It returns
an array of possible servers and it is your task to try each them
in turn in order to find a valid one (see section ktserv).
char **servers;
int n;
kt_LocateServer(& servers, & n);
printf("%d possible servers, first one : '%s'\n", n, servers[0]);
If you need to connect to a server (but you don't have to, you might simply want to be a handler that will be contacted directly), use the following call :
if (KT_OK == kt_OpenServerConnection(context, coord, "test")) { ...
Once this is done, you can register KRS, broadcast notices or issue requests.
Messages are created an destroyed with the function kt_MessageNew()
and kt_MessageFree(). One you created a message, you can change or
consult its following opaque fields : operation
(kt_MessageOperationGet() and kt_MessageOperationSet()) and
arguments (kt_MessageStringAdd(), kt_MessageStringGet(),
kt_MessageBytesAdd(), kt_MessageBytesGet()). If you need
to duplicate a message, use kt_MessageCopy().
msg = kt_MessageNew(); kt_MessageOperationSet(msg, "edit"); kt_MessageStringAdd(msg, "ktmon is dying"); kt_MessageFree(msg); ... char *str; kt_MessageStringGet(msg, 1, & str); /* get 1st argument */ free(str);
Before sending a message, you must specify to which Koalatalk Resource
Locator (KRL) it is destined. kt_KRLNew() and kt_KRLFree()
serve this purpose :
Kt_KRL aKRL; Kt_Message aMsg; aKRL = kt_KRLNew(context, "debugger"); kt_MessageSend(context, krl, msg); kt_KRLFree(krl);
If on the other hand you wish to declare your interest in a Koalatalk
Resource, use kt_KRObserve() or kt_KRHandle() :
void
cbObserve(Kt_Context context, void *userData, Kt_Message msg, CARD32 id)
{...}
...
CARD32 krs;
krs = kt_KROBserve(context, "edit.*", cbObserve, globalVars);
The value returned by both calls is a CARD32 number that will be passed
on to the specified callback when you receive a message destined to a
KRL matching the specification you supplied.
For example, suppose krs receives the value 101, and
a little later someone sends a message to the KRL "editor",
the function cbObserve will be called with its parameter id
equal to 101.
There are several types of callbacks understood by Koalatalk, they all have one of the following two signatures :
void cb1(Kt_Context context, void *userData, Kt_Message msg) or void cb2(Kt_Context context, void *userData, Kt_Message msg, CARD32 id)
The only difference is in the last parameter. It is needed only if the callback has been called because it matched a prealable call to the API. This includes :
id will contain the value returned by
kt_KRHandle());
id will contain the value returned by
kt_KRObserve());
id will
contain the value returned by kt_MessageSendWithReply());
The only case where you don't need to receive an id in parameter
is when a peer is sending you a direct message (as soon as you call
kt_Init(), you listen to a special "direct" file descriptor which
any other Koalatalk client can use to contact you directly).
Once you have both a message (Kt_Message) and a recipient
(Kt_KRL), you are ready to send a message. You have the
following alternatives :
kt_MessageSend();
kt_MessageSendWithReply());
kt_MessageSendToPeer()(18));
There is an addional function in the API, called
kt_MessageSendOnExit() that will allow you to store an
"emergency" message in the server (along with a KRL). This message will
be sent by the server if ever you die unexpectantly (i.e. you didn't
close your connection to the server by a call to
kt_CloseConnection()).
void *
cbHandler(Kt_Context ktContext, void *userData, Kt_Message msg, CARD32 id)
This must be the type of the callback used as parameter for
kt_KRHandle(). msg will contain the message sent to you
and id is the identity that was issued when you called
kt_KRHandle(). userData has the same value as the one
supplied in kt_KRHandle().
void *
cbDirect(Kt_Context ktContext, Kt_Message msg)
This must be the type of the callback used as parameter for
kt_PeerAddMsgCallback(). This callback will be called each time
a peer sends a message directly to you.
void *
cbReply(Kt_Context ktContext, void *userData, Kt_Message msg, CARD32 id)
This must be the type of the callback used as parameter for
kt_MessageSendWithReply(). msg will contain the message sent to you
and id is the identity that was issued when you called
kt_MessageSendWithReply(). userData has the same value as the one
supplied in kt_MessageSendWithReply().
void *
cbObserve(Kt_Context ktContext, void *userData, Kt_Message msg, CARD32 id)
This must be the type of the callback used as parameter for
kt_KRObserve(). This callback will be called each time
a peer sends a message directly to you.
void *
addInput(int fd, void *userData)
This must be the type of the callback used as parameter for
kt_Init(). This callback will be called by Koalatalk when
it wants you to add one of its file descriptor to the set.
userData is usually a variable that belongs to you and
allows you to retrieve this "global file descriptors set".
void *
removeInput(int fd, void *userData)
This must be the type of the callback used as parameter for
kt_Init(). This callback will be called by Koalatalk when
it wants you to remove one of its file descriptor from the set.
userData is usually a variable that belongs to you and
allows you to retrieve this "global file descriptors set".
A Koalatalk context.
When a request is sent and no handler can handle it at the moment, this type is used to specify how Koalatalk should react. It is an enumerate type of
KT_QUEUE : queue the request and send it when a handler
declares itself to `ktserv';
KT_START : see if `ktserv' knows of a suitable
handler in its protocols file and start it if it can find
one, otherwise fall back to KT_QUEUE case;
A Koalatalk Resource Locator.
A message.
A peer connection.
This is the type returned by most of Koalatalk's API calls. It is an enumerate type of
KT_OK : the call was successful;
KT_ERR : something went wrong;
Function: void
kt_AddUserFileDescriptor (Kt_Context context, int fd, void (*callback)(), void *userData);
Add a user file descriptor. The specified callback will be called if ever data on this fd is ready. The callback will receive userData as its unique argument.
Function: Kt_Status
kt_CloseConnection (Kt_Context context);
Close a connection to the server.
Return : KT_OK if successful, KT_ERR otherwise.
Function: void
kt_NextMessage (Kt_Context context, Kt_Message *msg);
Call the appropriate callbacks for msg.
Return : True if at least a callback was called,
False otherwise.
Function: void
kt_HandleInput (Kt_Context context, fd_set fds);
When you are managing your own main loop (i.e. you do the select()
yourself), you have to call this function if ever a file descriptor that
belongs to Koalatalk is active.
Function: Kt_Context
kt_Init (Kt_CallbackAddInput addInput, void *addInputUserData, Kt_CallbackRemoveInput removeInput, void *removeInputUserData)
Initialize Koalatalk.
If you wish to monitor personal file descriptors in addition to Koalatalk's,
you have to supply values to addInput and removeInput. If you
do this, Koalatalk will rely on you to
fd_set which you control totally.
fd_set.
select() yourself and ultimately call kt_HandleInput()
so that Koalatalk can take care of its own file descriptors.
Return : a Koalatalk Context, to be used in subsequent calls.
Function: CARD32
kt_KRHandle (Kt_Context context, char *krs,Kt_CallbackHandlerMsg callback, void *userData);
Declare as a handler for a Koalatalk Resource. callback will receive the message in argument, it must not free it (it will be automatically freed by the toolkit). krs is the Koalatalk Resource Specification to be handled (a regular expression).
Return : (CARD32) 0 if failure, or a unique identity
number that will be passed on to callback each time a message
matching krs is received.
Function: void
kt_KRLFree (Kt_KRL krl)
Free the memory allocated in krl.
Function: Kt_KRL
kt_KRLNew (Kt_Context context, char *krlName);
Define a Koalatalk Resource Locator with name krlName.
Return : A valid KRL (which must be free()'ed when no
longer needed).
Function: CARD32
kt_KRObserve (Kt_Context context, char *krs, Kt_CallbackServerMsg callback, void *userData);
Declare as an observer for a Koalatalk Resource. callback will receive a msg in argument, it must not free it (it will be automatically freed by the toolkit). krs is the Koalatalk Resource Specification to be observed (a regular expression).
Return : (CARD32) 0 if failure, or a unique identity
number that will be passed on to callback each time a message
matchine krs is received.
Function: Kt_Status
kt_KRUnObserve (Kt_Context context, CARD32 krId);
Stop observing the Koalatalk Resource krId (value returned by
a call to kt_KRObserve().
Return : KT_OK if successful, KT_ERR otherwise.
Function: void
kt_LocateServer (char ***serversFound, int *serversFoundCount)
Try to locate a server.
serversFoundCount will contain the number of servers that
could be found, or 0 if Koalatalk can't even make a guess.
You will find the coordinates to try in
serversFound[0..serversFoundCount-1], which you can pass directly
to kt_OpenServerConnection().
Note : this is just a guess, the coordinates returned might correspond to a dead server anyway.
Note 2 : each string serversFound[i] must be freed by the caller afterwards, and serversFound itself as well eventually
Function: void
kt_MainLoop (Kt_Context context);
Main loop to process messages and invoke callbacks accordingly.
Function: Kt_Message
kt_MessageArgCount (Kt_Message msg);
Return : the number of arguments in msg (first argument is number 0).
Function: Bool
kt_MessageArgIsString (Kt_Message msg, int argPos);
Return : True if argument number argPos is of type
String (note that the first argument is number 0).
Function: Kt_Status
kt_MessageBytesAdd (Kt_Message msg, void *bytes, size_t len);
Add a bytes parameter to a message.
Return : KT_OK if successful, KT_ERR if error.
Function: Kt_Status
kt_MessageBytesGet (Kt_Message msg, int argPos, char **returnedBytes, size_t *returnedLength);
Retrieve the argPos'th argument of this message, which must be of
bytes type. Note : returnedString must be freed with
free() when no longer needed. Argument 0 is the first argument.
Return : KT_OK if successful, KT_ERR if argument type is not bytes.
Function: Kt_Message
kt_MessageCopy (Kt_Message msg);
Allocate a new message, copy the content of msg into it and return it.
Return : a copy of msg (which can be safely freed
with kt_MessageFree() afterwards without interfering with the
returned argument).
Note : duplicating a message with a simple assignment will
not work. If the duplicated message is freed, the copy you
hold will become invalid as well.
Function: void
kt_MessageFree (Kt_Message msg);
Free a message.
Function: Kt_Message
kt_MessageNew ();
Allocate a new message.
Return : an empty message.
Function: Kt_Status
kt_MessageOperationGet (Kt_Message msg);
Return : the operation field for this message. Note : the returned value must not be altered. It is a direct pointer to Koalatalk's internal space. Argument 0 is the first argument.
Function: Kt_Status
kt_MessageOperationSet (Kt_Message msg, char *operation);
Set the operation field for this message.
Return : always KT_OK. Note : the string is duplicated in
Koalatalk's internal space, you can free it immediately after this call.
Function: Kt_Status
kt_MessageReply (Kt_Context context, Kt_Message query, Kt_Message reply);
Reply to the request request. reply is sent directly to the sender.
Return : KT_OK if the reply was sent successfully,
KT_ERR otherwise.
Function: Kt_Status
kt_MessageSend (Kt_Context context, Kt_KRL krl, Kt_Message krl);
Send a message to a Koalatalk Resource Locator, prealably defined by
a call to kt_KRLNew(). This message can be seen as a notice,
since it might reach several ktclients and no reply is expected.
Return : KT_OK if the message was sent successfully,
KT_ERR otherwise.
Function: void
kt_MessageSendOnExit (Kt_Context contex, Kt_KRL krl, Kt_Message msg);
Ask the server to store this message and to send it (just like if
sent with kt_MessageSend()) if ever it loses its connection to me
some other way than by kt_CloseConnection().
Return : KT_OK if the message was stored successfully,
KT_ERR otherwise.
Function: char *
kt_MessageSender (Kt_Message msg);
Return : the sender (ICE coordinates) of current message.
This function *can* return an empty string (but never NULL), since
kt_MessageNew() is invoked without any Kt_Context in it, and
is therefore a "standalone" function. Do not modify or free the returned
value, it belongs to Koalatalk.
Function: Kt_Status
kt_MessageSendToPeer (Kt_Context context, char *peer, Kt_Message msg);
Send msg to peer identified by peer, a string that contains
valid ICE coordinates (e.g. tcp/indri.inria.fr:2592).
Return : KT_OK if msg was sent successfully,
KT_ERR otherwise.
Function: CARD32
kt_MessageSendWithReply (Kt_Context context, Kt_KRL krl, Kt_Message msg, Kt_Disposition disposition, Kt_CallbackHandlerMsg callback, void *userData);
Send a message and expect a reply.
Return : (CARD32) 0 if failure, or a unique identity
number that will be passed on to callback each time a message
matchine krs is received.
Function: Kt_Status
kt_MessageStringAdd (Kt_Message msg, char *string));
Add a string parameter to msg.
Return : KT_OK if successful, KT_ERR if error.
Function: Kt_Status
kt_MessageStringGet (Kt_Message msg, int argPos, char **returnedString);
Retrieve the argPos'th argument of msg, which must be a string.
Note : returnedString must be freed with free() when no
longer needed.
Return : KT_OK if successful, KT_ERR if argument
type is not a string. If something wrong occured, returnedString
will be an empty string.
Function: void
kt_NextMessage (Kt_Context context, Kt_Message *msg);
Return : the next message received from Koalatalk (msg
will be allocated by the toolkit, you need not call kt_MessageNew()).
Function: kt_Status
kt_OpenServerConnection (Kt_Context context, char *serverLocation, char *identity);
Open a connection to the server specified by id (possibly obtained
from a call to kt_LocateServer().
Return : KT_OK if connection could be opened successfully,
KT_ERR otherwise.
Function: Kt_PeerConnection
kt_PeerOpenConnection (Kt_Context context, char *{peerLocation});
Open a direct connection to a peer. peerLocation is a string that
contains valid ICE coordinates (e.g. tcp/indri.inria.fr:2592).
Return : An identifier to be used to send messages directly to this peer.
Function: IceConn
kt_PeerReturnIceConnection (Kt_PeerConnection peer);
Return : The IceConn descriptor for this connection.
Function: int
kt_PeeReturnIceProtocol (Kt_PeerConnection peer));
Return : The ICE protocol used to talk with this peer.
Function: IceConn
kt_ReturnIceServerConnection (Kt_Context context);
Return : The IceConn descriptor of the server.
Function: int
kt_ReturnIceServerProtocol (Kt_Context context);
Return : the protocol used to talk with the server.
Function: char *
kt_ReturnIdentity (Kt_Context context);
Return : a string containing my identity. Note : do not modify this string, it is a pointer to Koalatalk's internal name space.
Function: void
kt_SetServerErrorHandler (Kt_Context context, void *handler);
Define the handler to be called if the connection to the server is lost.
Function: void
kt_UnInit (Kt_Context ktContext)
Uninitialize Koalatalk
`ktmon' stands for Koalatalk Monitor. It is a tool that lets you play with the Koalatalk without the hassle of writing an application and going through the process of compilation for simple examples. Its syntax is the following :
$ ktmon -help
Usage : ktmon
-autoConnect
-observed <value>
-handled <value>
-sendToKRL
-isNotice
-krl <value>
-arg1 <value>
-arg2 <value>
-arg3 <value>
-operation <value>
-server <value>
Each of these keywords has its equivalent as a resource. As a standard
Motif application, `ktmon' will read its application defaults in
the usual places. Its class name is Ktmon (19).
These parameters allow you to set certain values on startup, they are all facultative.
autoConnect : try to conect to the server on startup
(otherwise, wait for the user to push the Connect button);
observed koala.* : `ktmon' will observe the KRS
koala.*;
handled koala.* : `ktmon' will handle the KRS
koala.*;
sendToKRL : destination is a KRL (destination is a peer
otherwise);
isNotice : sent message will be a notice
(handler by default);
krl koala : set the KRL to koala;
arg1,arg2,arg3 string : set the arguments;
operation edit : set the operation field to edit;
server tcp/indri.inria.fr:2666 : define the server to use;
$ ktserv -help
Usage: ktserv 1.1beta
[-help] This help
[-debug <n>] Set debug level
[-icedebug] Turn on ICE debugging
`ktserv' uses/consults the following environment variables :
$DISPLAY : if this variable is set, `ktserv'
will store its coordinates in properties of the referenced display when
it is started, so that ktclients can find it;
$KTSERV_HOST : if this variable is set, it must contain the
hostname on which ktserv is currently running. It will be used to
consult the local portmapper in order to retrieve the full ICE coordinates
of the server. If it is not set, the default behavior is to use the hostname on
which this ktclient is running;
stdout a summary of the known
protocols;
tcp
connection;
As of current version (1.34beta), when `ktserv' is launched, it will store its coordinates in the following places :
ktserv will register itself as a regular RPC service
in the local portmapper (the one running on the machine where it
was launched). Afterwards, the applications can locate it by
simply setting the variable $KTSERV_HOST to the name of this
machine;
$DISPLAY is set and it can open a connection to the
referenced display, it will open an invisible window on it and store its
coordinates in properties of this window. The window id is stored in a
property on the root window. If `ktserv' dies, so will the window
and next time a ktclient tries to read the property off the
invisible window, it will fail;
Since Koalatalk handles several file descriptors, the usual method
(XtAddInput()) is not appropriate here, so a slightly more
complex scheme has to be used. In such a case, Koalatalk will rely on
you to handle the set of file descriptors, but it needs to know what
function (belonging to you) it has to call each time it wants to add or
remove a file descriptor from this set. This information is supplied
with kt_Init().
A typical section (syntaxically incorrect, but you get the idea) of code
to replace XtMainLoop() would be :
static void
busAddInput(int fd, void *userData)
{
GV gv = (GV) userData;
FD_SET(fd, & gv -> fds);
}
static void
busRemoveInput(int fd, void *userData)
{
GV gv = (GV) userData;
FD_CLR(fd, & gv -> fds);
}
void
guiHandleXInput(GV gv)
{
XtInputMask xim;
XEvent event;
while (0 != (xim = XtAppPending(gv -> appContext))) {
XtAppNextEvent(gv -> appContext, & event);
XtDispatchEvent(& event);
}
}
int
main()
{
context = kt_Init(busAddInput, gv, busRemoveInput, gv);
gv -> xfd = XConnectionNumber(XtDisplay(toplevel));
FD_ZERO(& gv -> fds);
/* Add your own file descriptors */
busAddInput(gv -> xfd, gv);
/* Koalatalk will add its file descriptors silently */
while (1) { /* this is the main loop */
int ready;
fd_set fds = gv -> fds;
ready = select(kt_getdtablesize(), & fds, 0, 0, 0);
if (ready > 0) {
if (FD_ISSET(gv -> xfd, & fds)) /* an X event */
guiHandleXInput(gv);
else
kt_HandleInput(gv -> ktContext, fds); /* a Koalatalk event */
}
}
[1]
The KoalaBus Group Communication Software, Programmer's manual
Cedric Beust, Colas Nahaboo, 1992
[2]
Making applications cooperation easier : The KoalaBus experience
Cedric Beust, 1993
[3]
Introduction to the Sunsoft's Tooltalk Service
A White Paper by Ricki Franker, 1991
[4]
Tooltalk's Programmer's Guide
Sun Microsystems, December 1992
[5]
Object Oriented Programming, an evolutionary approach
Brad J.Cox, 1986
[6]
Objective C, Object-oriented programming techniques
Lewis J. Pinson, Richard S.Wiener, 1991