|
Outgoing messages are high-level entities that allow to track the progress of the physical message from the sender's perspective, to check the message status and to obtain the potential reply content.
Outgoing messages are used as handles to the internal agent resources and the agent participates in their creation - the concrete form of this participation depends on the programming language, but the scheme is always the same: the user code asks the agent to send a message and obtains from the agent an outgoing message object for further tracking.
Note:
If the agent can eagerly determine that the message cannot be successfully transmitted because the target connection cannot be established, the sending is terminated at that early stage and the outgoing message is not created at all. The error is then reported to the user code in the form of an exception. The process of establishing the connection - or determining that it cannot be done - can involve some system-level delays, which can be additionally increased by the automatic retry mechanism.
The target connection has to be provided when the message is sent. In a typical case the form of the target corresponds to the resolved listening target that was obtained at the server (receiver) side, but it is also possible to send outgoing message to the remote agent over the connection that was first established by that remote agent - further chapters provide explanations of this possibility.
Messages are sent to the named destination object, which is assumed to be known by the target agent - in other words, the destination object needs to be registered at the remote agent (or the appropriate any object needs to be established), otherwise the message will be rejected.
In addition to the target connection and logical destination object name, the message has a name as its property. The name of the message is supposed to be understood by the message handler implementation at the server side. Object names together with message names can provide an object-based abstraction in the distributed system, where messages correspond to method calls and message names are used by the object implementation to choose the appropriate service.
The message can carry a parameters object as its payload. This payload can be empty.
The outgoing message begins its life when the user code asks the agent to send a message on its behalf and from that very moment the outgoing message undergoes a sequence of state transitions depending on transmission and the remote processing progress.
The possible states of the outgoing message are:
The possible state transitions are explained in the following diagram:
The last three message states (replied, rejected and abandoned) are collectively known as ``completed'' - that is, no further transition is possible and the user code need not wait any longer with its own processing, if it is determined by the message progress. It should be noted, however, that for some types of interactions where the remote site is not expected to send back any reply, the transmission is already the last expected step. In fact, these two conditions - transmission and completion - are important part of the idiomatic handling of outgoing messages and will be stressed in code examples.
It should be noted that all state transitions happen due to the progress performed by background agent threads. This means that even though the user code can safely obtain the current message state, it does not mean that this state is stable. For example, the client should not expect to see the posted state - even though such a state is really part of the whole message lifetime, it might be so short that the user code should not depend on capturing it. For another example, if the complete client-server interaction leads to the replied state (that is, from posted through transmitted to replied), the user code should not depend on the ability to capture and observe the transmitted state, even though such state is certainly part of the whole process.
In order to allow the user code to synchronize itself with the message progress, it is possible to wait for some particular processing phase - two such wait conditions are defined that relate to the ``layers'' of states in the above diagram:
Synchronizing with one of these two conditions is part of the typical use case for the code that sends the outgoing message, but it is important to note that since the message is being handled by the agent in the background, it is not necessary for the user code to block immediately after posting - the whole advantage of the asynchronous nature of messaging in YAMI4 is that the user code is free to continue with other processing while the message undergoes its state changes. In particular, it is possible to post many messages, possibly to different targets, and allow them to proceed in parallel.
In some particular applications there was a need for even more decoupled message processing, where the code that sends the message is not necessarily in the same location as the code that is going to receive its notifications, or where waiting on message completion is not allowed anyway due to non-blocking requirements of the given application. This was mostly relevant in GUI programs, where instead of potentially blocking waiting for message reply it is more convenient to receive the reply asynchronously and have it delivered similarly to other external events. For this purpose the asynchronous outgoing message notification was introduced in C++, Java and .NET libraries - see code examples in further sections.
The flow control mechanisms apply to outgoing messages - the user code that tries to post a new message might be blocked until there is enough place in outgoing message queues. Two configuration parameters, outgoing high and low water marks, can be used to control the speed of message creation. In a typical system which is not saturated but allows for some traffic peaks, the outgoing message is created immediately and placed in the outgoing queue of the respective channel.
Note:
If the parameters object is provided for the outgoing message, it is completely serialized at the very beginning of the whole process, before the control returns back the user code. There is no further association between the parameters object and the outgoing message and the parameters object can be destroyed or reused without any impact on the outgoing message even if the message was not yet physically sent. In other words, there is no need to artificially extend the lifetime of the parameters object until any particular message state is achieved. When the message is posted, the parameters object can be immediately destroyed or reused.
The following subsections present code examples that explain the way of using outgoing messages in each programming language.
In all examples it is assumed that "my_object"
is registered at the target agent (see the previous section for an explanation on how it can be done) and that the "do_something"
name can be interpreted by the server to perform the requested action with the given ``content'' parameters object.