Technical implementation


How does it all work?

Every application using OttoBus establishes a network connection to multicast address 224.0.0.1 and port 0xbabe. This connection is established transparent to the user, so you don't have to worry about it.

Publishing a message comes down to sending the message to the multicast address and port: all applications listening on that address and port will receive the data. This mechanism results in one single message being sent for every message publish call, regardless of how many listening applications are active.

Multicast messages are limited to 64K bytes (by design), so larger messages are broken down into smaller fragments to be sent over the network. Listening applications re-assemble the fragments into the original message.

One drawback of this implementation is that normally multicast packets are not routed. This means that in practice, OttoBus communication will usually be limited to one network segment.

OttoBus architecture

The overall architecture of OttoBus is like this:

There are five layers in this architecture:

  1. Network layer
  2. Connection layer
  3. Transport layer
  4. Message layer
  5. Application layer

Network layer

This layer is implemented by the underlying network interfaces in Java. The actual network communication is done at this level: DatagramPacket objects are transferred to and received from the network.

Connection layer

This layer consists of one single class: OttoBusConnection. This class is normally instantiated only once. That single object is responsible for:

All network specific code is located in this single class.

Transport layer

This layer consists basically of two classes:

In addition to these two, there is a helper class OttoBusFragment That class is used to hold fragments of a larger data message while not all fragments have been received yet. The single instance of OttoBusReceiver manages a cache of this fragments until either all fragments of a given message have been received, or until a timeout expires (after which all fragments for that message are removed from the cache)

This is the layer where (potentially very) large byte arrays are broken up into smaller fragments during transport and re-assembly at the receiving end. The classes in this layer have no knowledge at all about the data inside the byte[] objects: they just add a wrapper around them to handle fragmentation and re-assembly.

The re-assembly code in the current implementation is probably not the fastest possible, but it is designed to prevent problems that might occur as a result of deliberately malformed fragments.

Theoretically, when one fragment arrives, the software could already allocate a byte[] that is large enough to hold the entire re-assembled packet. However, such an approach would allow the trivial DOS attack where one attacker sends out fragments for different messages, each 0xFFFFFFFF bytes long. If OttoBus were to allocate the memory upfront, probably even the first such message might consume all available memory of the receiving system (4 GBytes!)

This layer is also the place to implement re-try algorithms in future releases: instead of simply dropping all fragments of an incomplete message, the OttoBusReceiver object could notify the OttoBusSender object to send out a request for a re-transmission of (part of) a message. Likewise, the OttoBusReceiver object would receive such requests for re-transmission and could notify the OttoBusSender object to re-transmit the requested data.

Message layer

This layer mainly converts OttoBusMessage objects to and from byte[] objects.

The outgoing direction consists of a number of OttoBusPublisher objects that simply convert an incoming OttoBusMessage object into a byte[] object. There is only one OttoBusSender object, but there typically are many OttoBusPublisher objects. In fact, there are as many as there are created by the application (which can mean that there can be multiple OttoBusPublisher objects for the same subject)

The actual conversion between OttoBusMessage objects and byte[] is handled by methods of the OttoBusMessage class. Each OttoBusMessage consists of exactly two OttoBusField objects:

It is up to the individual OttoBusField objects to convert themselves to and from byte[] objects.

The incoming direction consists of two stages:

The final step in the reception procedure is when the OttoBusSubscriber objects notify their IsOttoBusSubscriber clients by calling their consume() methods.

Application layer

This layer is the actual application using the OttoBus package. These applications communicate with OttoBusPublisher and OttoBusSubscriber objects using OttoBusMessage objects.

Known limitations

The current implementation has no provisions for requesting re-sending of fragments. If one fragment is missed by a subscriber, eventually all the fragments of that message will be discarded (after a timeout)

Unique message ID's are generated based on the current system time of the start (or initialization) of an OttoBus application. This has the potential of duplicate message ID's if two or more different applications are started more or less simultaneous. The author has tried to create such a situation with small test applications, but was unable to create a duplicate ID for different applications.

The Java interface reports the current system time in milliseconds, but probably this information depends on the granularity of updates of the system time by the operating system (and is therefor system dependent)

There are ways of avoiding such clashes, but at this time the author believes the extra development effort might be better spent on other features.


OttoBus home

Last Update: 16 September 2001