Example index

Minimal UM Publisher Example

This example demonstrates the minimal code to publish a message using UM. There is a corresponding minimal subscriber example at TODO.

When reading the documentation below, click on any line number to display the pure source listing on the right side of the screen. Similarly, if you click on a line number on the right side of the screen, the corresponding documentation will be displayed on the left.

There is one program source file:

MinSrc.java

The example source code is organized as a Java class.

Initialization

The first phase of any UM program is initialization, where UM objects are created. The first object to be created is a UM context object:

00011      /*** Initialization: create necessary UM objects. ***/
00012  
00013      ctx = new LBMContext();

A context can be thought of as an "instance" of UM. It contains a worker thread which maintains internal state and reacts to socket events. Most UM applications create a single context instance which manages the publishing and receiving of messages over many topics, although there are less-common use cases which call for multiple context instances within a single process (see TODO).

Note that exceptions are not handled. With this example program, any UM errors will lead to the program aborting with an unhandled exception. A real application require a different design.

The next object that needs to be created is a "source" object. Creation of a source object is a two-step procedure: first create a "topic" object, specifying the topic name, and then create the "source" object, which is the object used subsequently to send messages.

00015      {
00016        LBMTopic topic = nil;
00017        topic = new LBMTopic(ctx, "Greeting");
00018        src = new LBMSource(ctx, topic);
00019      }

The first thing to note is the use curly braces to create a block. This is not necessary, but is done in this example to emphasize that the topic variable is only used as a bridge between the following two method calls. The LBMTopic() method allocates a topic object internally inside the context and returns a handle to that object. However, there is only one thing that can be done with a topic object: use it to create a source object. Once that source object is created, the application no longer needs to retain the topic object. It is unique among UM objects in that the application does not need to manage or clean up topic objects explicitly -- e.g. there is no topic.close() method.

Note that it would be unusual for a real application to create a single topic for all its communication needs. Most real-world applications would create several sources, all within the same context. Just be aware that each one requires the same new topic/new source pairing.

The final step during initialization is to wait for any subscribing applications to discover this new source:

00021      Thread.sleep(1000);

This process is called "topic resolution", and takes a non-zero amount of time. If the publisher sends a message before the discovery process completes, some subscribers will not receive the message. This example takes the very simple approach waiting for a full second before sending its first (and only) message, but while this is usually more than enough time, there are scenarios where more time is needed (e.g. if large numbers of source objects are created all at once). Many users want a simple, fool-proof method for knowing when it is "safe" to send (i.e. when all pre-existing receivers will successfully receive the message); unfortunately, no such one-size-fits-all approach exists. The subject is addressed in more depth in TODO.

Publishing

Now that initialization is complete, it is time to send a message:

00024    /*** Send a message. ***/
00025  
00026      src.send("Hello!".getBytes(), 7, LBM.MSG_FLUSH | LBM.SRC_BLOCK);

Note that the string "Hello!" only has 6 characters, but a 7 is passed as the number of bytes. This is to ensure that the null string character is included in the message. Also note the inclusion of the FLUSH and SRC_BLOCK flags.

The SRC_BLOCK flag technically does not need to be supplied since the default behavior is blocking, but this example supplies it anyway just to emphasize the fact that the application expects the send to block (go to sleep) if a blocking condition is encountered. For an example of non-blocking sends, see TODO.

The FLUSH flag is used to override the default UM behavior of using implicit batching. Batching is the process of combining multiple outgoing messages into a single network packet. This is done for efficiency so that high message rates can be achieved. However, using implicit batching can introduce message latencies of many milliseconds, so it is not unusual for UM applications to send with the FLUSH flag to push the message onto the network immediately. The tradeoff with this simplistic approach is that at high message rates, a significant load is imposed on both the sender and the receiver. For a somewhat more complex "intelligent batching" example, see TODO.

The UM API is thread-safe, meaning that the application might have multiple threads concurrently calling src.send(). However, be aware that there are situations where those threads will contend for internal locks (mutexes), leading to decreased parallelism and throughput. Source threading issues are explored in TODO.

Cleanup

When its time to delete UM objects, certain guideliens should be followed. There are several possible asynchronous operations which might not yet have completed since the last send, so a short sleep is included:

00029    /*** Cleanup: delete UM objects. ***/
00030  
00031      Thread.sleep(3000);

For example, if the FLUSH flag is not supplied with the send method, the implicit batcher might be holding on to one or more messages, and must be allowed to time out. Or a UDP protocol might be used and a subscriber might have experienced "tail loss", requiring a timeout/retransmission sequence to take place. As with source creation, many users want to know how long to wait before deleting objects, and as before there is no perfect answer. For example, the default NAK generation interval is 10 seconds, suggesting that the linger should be at least that long. But a 10-second wait before allowing a program to exit may not be acceptable, and is rarely needed. Many users take the attitude that if a subscriber is still trying to recover loss packets after 2 or 3 seconds, then it is better to just let that subscriber experience unrecoverable loss.

Unlike most common practices in Java, most UM objects should be explicitly closed when they are no longer needed. Since most UM objects contain an active component, just letting them go out of scope to be garbage collected is not an appropriate way to dispose of them.

When deleting UM objects, order of deletion is important:

00033      src.close();
00034      ctx.close();

In general, timers should be cancelled first. Then source and receiver objects should be deleted, then context objects, and lastly event queues. These object deletions can become somewhat complex if event queues are used; see TODO for more explanation.

Advanced Topics

licensing: TODO

configuration: TODO

anything else? TODO