Note: FMP 3.0 is coming soon.
Note: there are tutorials for beginners. Please check them out on the Tutorials page.
Fast Messenger Programming enhances Object-Oriented Programming (OOP). In traditional OOP, an object (caller) invokes a method on another object (callee) when the caller needs the callee to do some work. For example, in List 1, the object that contains line 100 is the caller,
calculator is the callee,
multiply is the method, and
product is the outcome of the work.
BigInteger product = calculator.multiply (n1, n2);
This is the only way to invoke a method in OOP. Fast Messenger Programming (FMP) expands on OOP’s capability by introducing active object, a type of virtual object, to support handling work that involves concurrency.
Because active objects are virtual objects that do not exist in any OOP languages, FMP also introduces messenger object, a type of FMP system object, to host and realize active objects. A messenger object provides methods for callers placing variant types of call requests to invoke functions on active objects, it can also implements functions of active objects by using other objects or programming features. As a result, active objects become functional virtual objects and behave as described by the active object specification below.
List 2 illustrates the various types of function invocations on an active object in FMP.
IMessenger messenger = new Messenger (); messenger.registerReceiver (calculator, "calculator", "multiply", "divide", "pow"); 1) IDeferred deferred = messenger.callFunction ("calculator:multiply", n1, n2); 2) boolean accepted = messenger.sendMessage ("calculator:multiply", n1, n2); 3) boolean accepted = messenger.publishMessage ("report:multiplicands", n1, n2);
Later sections of this document will further explain the entries in this list.
Active object specification
Each active object has a name that can be used to uniquely identify it within the context it lives and operates (that is, within a messenger object).
Each active object has a set of functions that any object or active object can invoke. Each function has a name that is unique among the active object’s other functions.
Every function can take one or many objects as input arguments and generates a single object as return value. Input arguments and return value are optional though. A function could take no input argument and/or create no return value.
When an active object receives a call request to invoke one of its functions, it executes the function asynchronously (independently) — that is, without coordinating with the caller in terms of synchronicity. From caller’s point of view, the caller is not blocked and is free to do anything else while the function is being executed.
If new call requests come while an active object is executing one, these requests are put on hold until the active object finishes and becomes available again. In short, an active object executes multiple call requests one by one (sequentially) in the order of their arrival.
Identifier naming convention
Each messenger object, active object, and function has a name (Id). FMP does not specify any naming convention, but the following are good characters for compatibility of names across platforms and languages: letters, digits, dots (
.), hyphens (
-), and underlines (
FMP defines separators for fully qualified names. A double colon (
::) is used to separate a messenger name to the left and other elements to the right. A single colon (
:) is used to separate an active object name to the left and function name to the right, as shown below.
In a clear context, a fully qualified name may omit unnecessary names and separators. For example, a caller does not have to specify a messenger name when it places a call request using a particular messenger object.
In most cases, a name without any separator is considered a name of an active object.
Note: FMP reserves asterisk (
*) and question mark (
?) as wildcard characters for future use. Developers should avoid using other special characters, as they might cause escaping issues in certain environments.
Active object implementation guideline
A messenger object serves as a virtual machine that realizes active objects. It provides methods for callers placing variant type of call requests to invoke functions on active objects; it can also enforce independent and sequential function execution per active object. But a messenger object cannot implement functions of active objects by itself, because they are user functions.
This guideline introduces a few typical ways for developers to implement functions of active objects, and for software vendors to implement messenger objects. A messenger implementation object can use active object implementation objects to virtually execute functions for active objects as if the active objects themselves execute the functions.
Using an object to implement an active object is very straightforward; an active object can be viewed as an object plus independent and sequential function execution. A messenger implementation object can perform this kind of execution without user inputs.
Even if functions and methods have a one-on-one relationship at object level, there are still different types of mappings between functions and methods. All functions of an active object could be mapped to a single method on an implementation object; or each function could be mapped to a counterpart method.
List 3 shows what the former type of function mapping looks like in code, where a single method on an active object implementation object handles requests for all functions. The requested function name is passed to the single handling method. The method then chooses appropriate logic according to the function name.
Object handleCallRequest (String functionName, Object args);
FMP can be implemented with both OOP and non-OOP languages as long as software vendors find creative ways to realize active objects and make them act as described by the active object specification above.
Messenger object specification
A messenger object provides methods for anyone to access active objects. There are three major methods through which any object or active object can place a call request to invoke a function on an active object. The
callFunction() is typically used for imperative programming. The
publishMessage() is typically used for event-driven, message-passing, and dataflow programming. The
sendMessage() is a hybrid for both types of programming.
sendMessage() employ direct addressing: a caller should provide a name for the target active object and function.
publishMessage() method employs route-based addressing: a caller provides the name of itself rather than of the target. In order to figure out target active objects, the messenger object depends on routes, each of which contains the name of a sending caller and function, and the name of a target active object and function. Routes can be provided to or removed from the messenger object by any third-party object at any runtime moment.
callFunction() & sendMessage()
Any object or active object can use the
callFunction() method (Line 204 in List 2, above) or
sendMessage() method (Line 206) on a messenger object in order to place a call request invoking a function on an active object. The call request specifies a name for a target active object and function, and a list of objects as arguments.
If the caller’s goal is to invoke the function on the active object but not to know when the function finishes, nor a return value, the caller should use
sendMessage(). Otherwise, the caller should use
callFunction(). This is because
callFunction() returns an
IDeferred object; the caller can use this object to query whether the function execution is finished, without blocking itself, or it can block itself to wait until the function execution is finished. In either case, the caller can obtain a return value through the
publishMessage() method (Line 208 in List 2, above) is different from the others in terms of the way it addresses the target active object. In the route-based addressing, think of a function name as a message port whose value specifies a type of message. A good analogy is to think of an active object’s name as an IP address of a host machine, and a function as a socket port. List 4 shows route-based addressing used by
messenger.registerRoute ("report:multiplicands", "calculator:multiply");
If a route in Line 400 is registered by the same messenger prior to Line 208 (see List 2 above), the messenger object uses the route to determine a target active object and function for the call request placed by
publishMessage() at Line 208. As a result, the call request in this example will be routed to
A messenger object forms a namespace in which every active object is unique. FMP allows multiple messenger objects to join together and form a large virtual namespace. The virtual namespace is used to catch “missed” call requests.
The local namespaces take high priority to the virtual one. When a messenger object receives a call request, it first looks inside of its own namespace. If no target active object can be identified, the messenger object passes the call request to the virtual namespace. As a result, a call request placed on a messenger object may end up received by an active object inside another messenger object.
A messenger object can act as a single active object within another messenger object.
You can connect multiple messenger objects to work together without sharing their namespaces. For example, an object or active object may place a call request on a messenger object with a target on another messenger object. For example,
This can be applied to routes as well. A route with names of different messenger objects can route a message published from an active object residing in one messenger object to an active object residing in another messenger object.