The Java Business Integration specification (Java Specification Request 208) defines a standard means for assembling integration components to create integration solutions that enable a service-oriented architecture (SOA) in an enterprise information system. These components are plugged into a JBI environment and can provide or consume services through it in a loosely coupled way. The JBI environment then routes the exchanges between those components and offers a set of technical services.
A component is the main detail a user or developer faces when working in the JBI environment. Understanding how a component interacts with the JBI environment is just as important as understanding the environment black-box itself.
The JBI environment
JBI defines a container that can host components. Two kinds of components can be plugged into a JBI environment:
- Service engines provide logic in the environment, such as XSL (Extensible Stylesheet Language) transformation or BPEL (Business Process Execution Language) orchestration.
- Binding components are sort of "connectors" to external services or applications. They allow communication with various protocols, such as SOAP, Java Message Service, or ebXML.
JBI is built on top of state-of-the-art SOA standards: service definitions are described in WSDL (Web Services Description Language), and components exchange XML messages in a document-oriented-model way.
The JBI environment glues together the JBI components by acting as a message router to:
- Find a service provided by a component
- Send a request to a service provider
- Manage the message exchange lifecycle (request, response, acknowledgements, etc.)
It also provides a set of services (support of naming context, transactional context, etc.) to the components.
In addition, the JBI container (shown in Figure 1) manages, through a rich management API, the installation and lifecycle management of the components, and the deployment of artifacts to configure an installed component (for instance, deployment of XSL stylesheets to a transformation service engine).
JBI components are the base elements composed by the JBI container to create an integration solution. The components are plug-ins for the container and are considered "external." As a Java EE (Java EE is Sun's new name for J2EE) container hosts Enterprise JavaBeans (EJB) components, or as a portal hosts portlets, the JBI container hosts integration components. You can write your own components, or you can use components written by someone else, in the same way you write a portlet or use an existing one.
A JBI component comes with several main objects represented by the following SPI (system programming interface):
Componentobject, with which the container interacts to retrieve component information (such as a services description)
LifeCycleobject, used by the container to manage the component's lifecycle
ComponentContext, given by the container to the component, for communication with the JBI environment
Additional objects are:
Bootstrapobject, which provides all operations required at install/uninstall time
ServiceUnitManagerobject, used to manage deployment of artifacts on a component
A component is packaged as an archive (a zip or jar file). This archive contains the component's classes, the required libraries, and a descriptor file.
To plug a component into a JBI container, use the management API provided by the container. This API allows you to provide to the container your component package's location. Then, the container processes the component archive and installs the component.
From the component viewpoint, two different phases are defined:
- The installation phase, in which the JBI container installs the component and plugs it into the bus
- The execution phase, in which the JBI container interacts with the component
During the installation phase, the component must perform all extra processes needed for its execution, such as the creation of mandatory folders or the installation of a database. As illustrated in Figure 2, the
Bootstrap object completes the installation and receives an
onInstall() event from the container with an associated
InstallationContext object, which provides to
Bootstrap some installation information (the path of the installation, a naming context, etc.).
The container starts, stops, and shuts down the component. As shown in Figure 3, the container initializes a component by passing it a
ComponentContext, which is the entry point to the JBI environment. While the component runs, it interacts with the JBI environment through this
The component can consume services (exposed on the bus by other components) by sending messages to a service provider. The component can also act as the service provider, accepting and processing such messages, and returning an answer to the consumer through the bus.
To illustrate the interactions between a service consumer and a service provider, let's take a simple request-response exchange as an example. The corresponding message exchange pattern (MEP) is an InOut exchange pattern, as defined in the JBI specification (see section 5.4.2). JBI supports four WSDL pattern exchanges: In, InOut, InOptionalOut, and RobustIn. Each pattern defines a particular exchange sequence.
Let's begin with service consumer interactions.
Once a component is running, it can find and consume the services registered in the JBI environment. Therefore, the component is in the role of a service consumer.
Find a service endpoint
ServiceEndpoint represents an address where a service provided by a component can be found. Several components can provide the same service (with eventually different implementations), but each of those components has a unique endpoint.
As shown in Figure 4, to find a service, the consumer asks its
ComponentContext for the list of all endpoints matching the service name by using the
getEndpointsForService(serviceName) method. The consumer then chooses the provider it wants to reach. If the consumer already knows the address of the provider that it wants to contact, it can retrieve the provider
ServiceEndpoint object by using a
getEndpoint(serviceName, endpointName) method.
Create a message exchange
To manipulate messages, the
ComponentContext provides a
DeliveryChannel object, which represents a bidirectional communication channel between the component and the JBI environment's message router (called normalized message router, or NMR). The
DeliveryChannel is in charge of message-exchange instantiation and is the path through which the messages advance to the NMR. The NMR routes the message to the component that provides the requested service. An exchange between the consumer and the provider is materialized by a
MessageExchange object. This object serves during the exchange's entire lifecycle.
When the consumer wants to initialize a new exchange, it asks a
MessageExchangeFactory (provided by the
DeliveryChannel object) to create a new
MessageExchange contains the actual message content and a set of metadata, such as the provider endpoint, the status of the exchange (active, done, in error), or identification of the exchange "owner" (the consumer or the provider). The consumer and provider share
MessageExchange during the exchange (request, response, etc.).
Send the message
Now that the consumer has instantiated a
MessageExchange, it can set on this object the message it wants to send to the consumer. The consumer has to set a
NormalizedMessage as the exchange's "in" message. The
NormalizedMessage is a JBI definition of a message. The consumer asks the
MessageExchange to create a new
Then, the consumer sets on this
NormalizedMessage the content of the message (an XML payload) and eventually some attachments. The consumer must set on the
MessageExchange the provider's
ServiceEndpoint (previously retrieved) and the name of the operation to be performed.
Note: The consumer can omit stipulation of the provider's
ServiceEndpoint and just specify a service name. In this case, the NMR will search all matching endpoints and choose one of them.
Finally, the consumer sends the
MessageExchange using the
DeliveryChannel. Figure 6 illustrates this entire process.
Once a component is running, it can also provide services. This component acts as a service provider.
Activate an endpoint
The provider must publish the services it wants to offer. As shown in Figure 7, the
activateEndpoint(serviceName, endpointName) method publishes the service publication. This method returns a generated
ServiceEndpoint object that references the new service in the JBI environment.
With publication completed, other components can access the activated services by finding the corresponding endpoint with their
Receive a message
Now that the provider has published some services, it can receive messages from other components (consumers). When a consumer sends a message to a provider, the message (
MessageExchange) is pushed in the message queue of the provider's
DeliveryChannel. The provider retrieves the received messages from the message queue by calling
accept() on its
Once the provider obtains a
MessageExchange, it can process it. The provider gets the "in" message (the
NormalizedMessage object set by the consumer), the name of the operation to perform, the payload of the message, and so on.
Send the response
If the operation requires an answer, the provider can set an "out" message on the
MessageExchange and send it again to the NMR via its
DeliveryChannel. The NMR routes the
MessageExchange to the consumer that previously initiated this
Close the exchange
After the provider sends its response, the exchange is nearly complete. The NMR routes the answer to the consumer, which receives it with an
accept() call on its
DeliveryChannel. The consumer processes the
MessageExchange's "out" content and then must close the exchange. To do this, the consumer sets the status of
DONE and sends it again to the NMR via its
send() method. The exchange is terminated, and the provider learns of this termination upon receiving the
MessageExchange with its
The following schema describes the whole exchange process.
Now it's time to see more practically how the components interact with the JBI environment. In this section, I show most of the code that needs to be implemented to create a simple Helloworld service engine. As shown earlier in this article, a JBI component is a set of objects that must implement some JBI interfaces. In addition, a descriptor file must be provided with this component. As just a few lines of codes are necessary to implement the
Component and the
ComponentLifeCycle interfaces, a single object can implement these two interfaces.
To process requests, there is no listener mechanism proposed by the JBI specification. To receive a message, we must block on an
accept() method. A good pattern is to create a separate object executed in another thread. This "listener" makes a loop on the
accept() method, avoiding the
Component to be blocked.
Component and ComponentLifeCycle
The object that implements the
Component and the
ComponentLifeCycle interfaces just registers the HelloworldService in the JBI environment, then creates and starts the
HelloworldListener thread, which processes the incoming messages: