JBoss ESB 5 Design Space
The core provides the basic building blocks for the foundation of an ESB:
Message : represents an individual input or output of a service, the content of which is interpreted by service implementation logic. A message does not carry context specific to a service invocation, which means that it can be re-used, copied, or manipulated across service invocations.
Exchange : represents a service invocation with a specific message exchange pattern (e.g. InOnly, InOut). The exchange provides a container for messages that flow into and out of a service as part of a service invocation. Unlike messages, an exchange represents a single instance of a service invocation and cannot be reused. State associated with an invocation (i.e. context) is maintained at the exchange level.
Channel : a channel represents an asynchronous, bi-directional communication contract with the core's message bus. Exchanges are sent via a channel and received through the channel. Delivery of exchanges through the channel is an event-based contract
Event : notification that a message exchange has changed state.
Handler : used to handle generated events. Handlers have visibility into all exchanges that are sent and received through a channel. These are grouped together into an order execution series known as a HandlerChain. Handlers provide a tremendous amount of flexibility and visibility into the processing pipeline for exchanges. To the extent possible, all exchange processing logic in the core will be implemented as handlers (e.g. service address resolution), thus providing the user/administrator with the ability to complement, override, or disable system functionality.
Service : from the perspective of the core, a service is simply a named address to which exchanges can be routed. The format of a service is javax.xml.namespace.QName. Service providers register services and service consumers invoke services using the service's QName.
The core can be divided into three distinct sections:
API : public interfaces exposed to all clients of the core module. Components that provide and consume services use the core API to do so. Please note that this API is distinct from a "client" API, which will be provided as a separate module and will provide comfort APIs like blocking send for request response, annotation-based injection of service references, etc.
SPI : public interfaces exposed to external modules that wish to provide services utilized by the core. Examples of this include a service registry and the underlying message bus.
Internal : core implementation classes which are not exported outside the core module.
The interface definition for Message can be found here.
A message is a container for data used as an input or output from a service. This message has three distinct sections for content:
Body : the main body, or payload, of the message. The base Message interface makes no assumptions about the content type, which can be a POJO, XML, input stream, etc. There is only one body per message instance, although the body itself can be a Map.
Properties : properties provide additional context around the content of the message. The ESB does not read or write properties contained in a message as this is the domain of the service consuming or producing the message.
Attachments : provide the ability to associate content with a message separate from the main body. The primary reason for including content as an attachment is to delay parsing of that content. One example would be a binary image that is referenced by the main body of the message. The attachment may be too large to be processed in certain services or service implementation may not be able to parse/interpret it.
At this point, there are no content-specific interfaces that inherit from Message (e.g. StreamMessage, XMLMessage), but it may be a good idea to introduce these types to allow for consistent processing within service components in the ESB.
The interface definition for Exchange can be found here.
Exchange represents an instance of a service invocation defined by a message exchange pattern. An exchange holds both context and content (in the form of messages) relevant to the invocation of a service and can be considered equivalent to a unit of work.
ExchangeContext : holds context for the exchange as string/value pairs. Context items include details such as the transactional characteristics of the exchange, authentication information, delivery details, etc. This is basically a map today, but will most likely be extended to include convenience methods to access common context properties.
ExchangePattern : identifies the message exchange pattern used for this exchange. The exchange pattern describes the sequence of messages exchanged during a service invocation. The list of acceptable patterns is currently constrained to InOnly and InOut, since most (if not all) service interactions can be suitably described using these two patterns. We may want to consider adding support for other exchange types (e.g. RobustInOnly) as well as allowing users to add their own patterns.
ID : each exchange carries a UUID represented as a String. This value is assigned by the ESB.
Service : the service being invoked represented as a QName. This is a logical address which is mapped to a concrete (or physical) endpoint on the bus by the ESB when the exchange is sent.
Messages : an exchange carries slots for three messages : in, out, and fault. A service consumer will set the in message before sending the exchange. A service provider will process the in message upon receipt of the exchange and then reply with an out/fault (InOut) or not reply at all (InOnly).
The interface definition for ExchangeChannel can be found here.
The primary role of an exchange channel is to serve as the communication contract for service consumers and providers. There are two paths for exchanges in a channel :
Sending : an exchange can be sent using the send() method on ExchangeChannel. A service consumer would send a new exchange to invoke a service. A service provider would send an existing exchange (i.e. an exchange it received) to return the output of a service invocation. The thread used to send an exchange currently blocks until all handlers have fired, but returns after the bus has accepted the exchange for delivery (i.e. the sending thread is not used to deliver the message to the receiving channel). It may make sense to return immediately and simply notify the sender via an event if a handler fails (see event discussion).
Receiving : exchanges are received by the handler chain associated with an ExchangeChannel. In most situations, the service implementation logic will reside in a handler at the end of the chain. The handler chain is invoked using threads managed by the ESB. The thread pool size and delivery priority will be configurable aspects of the core.
There are a number of helper methods currently anchored on ExchangeChannel that may belong elsewhere:
createExchange() : creates new exchange instances. If we plan on adding support for different exchange patterns, we may want to export this method to a builder/factory interface.
register/unregisterService() : this probably belongs elsewhere, potentially with a completely different type of API.
The interface definition for ExchangeEvent can be found here
Events are simply triggers that are received and processed by handlers. Each state of a message exchange has a corresponding event interface. If this seems a bit familiar, it's because this API is very close (read: inspired/stolen) to the Netty API. The reason to focus on events is that it decouples the sender of an exchange from any downstream activity which carries an indefinite wait time. Instead of consuming a thread to block on an exchange response, the sending thread is free to perform other work and the response will be delivered when it's ready via an event.
The interface definition for ExchangeHandler can be found here.
ExchangeHandler provides a processing hook for exchanges as they are sent and received through an exchange channel. ExchangeHandlers are fired on a thread controlled by the core and executed in an ordered, serial pipeline as they are listed in a HandlerChain. A convenience class, BaseHandler, is provided which implements ExchangeHandler and provides convenience methods for handling specific exchange events. This idea was shamelessly stolen from the Netty API.