Advertisements
HTML clipboard Every day, software professionals look for solutions to address integration problems in the business-to-business (B2B) and enterprise application integration (EAI) fields. These solutions are typically based on proprietary technologies, JMS platforms, or web services. Lately, a new technology has appeared: Java Business Integration (JBI). It offers a solution to the integration problem by defining a standard architecture where different components can be plugged in, in a cooperative context.
In this article, we will explore the main features of this technology, by developing a new component that can be plugged into the JBI environment. This component exchanges information with an external GSM modem to send a short text message. This is potentially useful because nowadays, many of us use short message service (SMS) to send and receive data like news, alerts, event notifications, and so on.
Before we start coding our component, we must first understand how JBI works.
JBI Overview
JBI provides an environment into which components can be plugged in. In this environment, the components collaborate by exchanging information in order to accomplish some tasks. The information is exchanged using a message exchange model according to the web service description language (WSDL) 2.0 or 1.1.
Figure 1. JBI environment
JBI components can be divided in two different types:
- Service Engine (SE)
- Binding Component (BC)
The Service Engine provides business logic and transformation services to other components and can consume services, too (for example, a service engine can be a XSLT engine).
A Binding Component provides connectivity to the world outside the JBI environment, supporting specific protocols. For example, a binding component can implement HTTP protocol and so on.
Even if we make this distinction between these two component types, in the JBI environment, from the developing point of view, they are the same. An SE and a BC can act as service provider, service consumer, or both at the same time.
The key concept that stands behind JBI is that the components don't exchange messages directly but instead use an internal component that is part of the JBI environment called Normalized Message Router or simply NMR. The NMR component acts as an internal router, forwarding messages to the right component; in this way it enables components (SEs and BCs) to work together. JBI is based on the normalized message concept, meaning that JBI internally uses a message structure that is technologically independent and conforms to the WSDL abstract message definition.
A normalized message is built by three different parts:
- The bare message: An XML document following the WDSL abstract message specification
- The metadata: Extra data associated to the message used by the JBI environment to route messages
- The attachments
All the components plugged into the JBI environment exchange messages with the NMR component using a DeliveryChannel, which is a bidirectional channel where SEs and BCs send and receive messages. Figure 2 shows the JBI environment with some components. The black connectors between components and the NMR represents the delivery channel.
Figure 2. A service engine and binding component in the JBI environment
One interesting feature of this environment is the capability to combine together different BCs and SEs to create a Composite Application. In this way, using some basic services and binding components, we can create a great variety of applications.
An important aspect we must consider while developing an SE or a BC is the component lifecycle. The lives of all components deployed are managed by the JBI environment. JBI manages component lives using a set of JMX management beans (MBeans); the most important administration tasks are:
- Installation of a new JBI component
- Lifecycle component administration
- Component deploy
The component lifecycle is built by several states, shown in Figure 3.
Figure 3. Component lifecycle
In order to be managed, a component must implement some interfaces that enable the component to be plugged in the JBI Environment. These interfaces are defined in the javax.jbi.component
package. We analyze these interfaces below:
InstallationContext
: This interface contains information used by the JBI component to perform installation and uninstallation tasks.- Bootstrap: This interface is called by the JBI environment during the installation or uninstallation phase. During the bootstrap phase, the component can create a directory or database table necessary to work properly. This interface contains several methods but we focus our attention to the
init
. As parameters, the init
method accepts the InstallationContext
used by the component to access JBI environment information (i.e component name and so on). ComponentContext
: This interface is used by the component to query the JBI environment where it is plugged in. Moreover, this interface is used by the component to activate its services and make them available to other components.ComponentLifeCycle
: This interface is used to manage its lifecycle. The methods reflect the different component states (stop, start, shutdown). The component life starts when the init()
method is called by the JBI engine. As a parameter, the init()
method accepts the ComponentContext
interface. Once the component is done with the init phase, the JBI engine calls the start()
method. During this phase we have to provide the business logic that it is necessary to start the component. In the same way, we have to provide the logic in the stop()
method called by the JBI engine when the component is about being stopped. Finally, in the shutdown()
method we have to release all the resource used by the component during its life.ServiceUnitManager
: This interface defines some component methods to manage the service unit deployment.Component
: This interface is called by the JBI environment to query the component for various pieces of information, such as the component lifecycle interface and the component service unit manager. In this interface are defined several methods, but the most important are getLifeCycle()
, which must return a valid implementation of the ComponentLifeCycle
interface, and getServiceUnitManager()
, which must return an implementation of the ServiceUnitManager
interface.
Once we have coded our component, if we want to deploy it we must describe it using an XML artifact where we specify our implementation of the Bootstrap
and the Component interfaces. We will see later how to do this.
We've described the main aspects of JBI, and now we can develop a component that can help us to understand more deeply how this all works.
SMS Library
Before starting coding the SMS-sending component described in the introduction to this article, we need to focus briefly on the way we can exchange information with an external modem. To do that, we develop a simple library based on theJava Communications (javax.comm) API. For a modem, we can use a simple mobile phone connected through a USB cable or Bluetooth. With either approach, we interface with the device using a COM port, so we can use javax.comm
. Figure 4 shows the library's UML class diagram.
Figure 4. Class diagram (click the image for a larger version)
We focus our attention the on the SerialConnection
class. This class uses ConnectionParam
s to receive the configuration parameters. The first step, therefore, is to open the COM port and set the right parameters. The code below shows how to do it:
try { System.out.println("Open port ["+param.getPortId()+"]"); portId = CommPortIdentifier.getPortIdentifier(param.getPortId()); } catch (NoSuchPortException ex) { Logger.getLogger(SerialConnection.class.getName()). log(Level.SEVERE, null, ex); throw new ModemConnectionException(ex); } // Open the serial port with 20 sec timeout try { port = (SerialPort) portId.open("SMSServer", 20000); } catch (PortInUseException ex) { Logger.getLogger(SerialConnection.class.getName()). log(Level.SEVERE, null, ex); throw new ModemConnectionException(ex.getMessage()); } // Enable Break signal notification port.notifyOnBreakInterrupt(true); // Enable Carier Detect notification port.notifyOnCarrierDetect(true); // Enable Data Available notification port.notifyOnDataAvailable(true);
To send a short message through a GSM modem, we have to use AT
commands. The SMSSender
class takes care that job, using the following command sequence:
ATZ AT+CMGF=1 AT+CSCA="Service Message center number" AT+AT+CMGS="Destination mobile number" >Message Text
Note that this command sequence may vary depending on your mobile phone.
For each command sent, we have to wait for the modem response before send the next command. The response can either be OK
, in which case we continue with the next sequence step, or an error, which requires us to reset the modem using CTRL + Z and start again.
Creating the Component
As described earlier, to develop a component we need to implement several interfaces: Bootstrap
, ComponentLifeCycle
, Component
, and ServiceUnitManager
. The UML class diagram in Figure 5 describes the classes used to build our component.
Figure 5. Component class diagram (click the image for a larger version)
required by the interface with an empty body, because we don't need to do any special work at bootstrap phase. SMSComponentLifeCycle
is the heart of this component. In the start()
method, we register our service endpoint and we start the MessageReceiver
thread to listen to incoming messages, so that we can handle them. The code below shows how to do this:
public void start() throws JBIException { logger.info("--- Starting component ["+ctx.getComponentName()+"]"); // Activate endpoint QName qServiceName = new QName(EndPointDesc.NS, EndPointDesc.SERVICE_NAME); serviceEndpoint = ctx.activateEndpoint(qServiceName, EndPointDesc.SERVICE_EP); logger.info(" --- Activated endpoint [" + serviceEndpoint + "]"); // Starting message receiver msgRcv = new MessageReceiver(channel); (new Thread(msgRcv)).start(); logger.info("--- Component ["+ctx.getComponentName()+"] started."); }
As we said during the JBI description, the Normalized Message Router (NMR) through the DeliveryChannel
exchanges information with our component using normalized messages. In the MessageReceiver
class, we handle the incoming message and then we convert it into AT commands to send a short message. This class is a thread that continuously listens for messages. The first step in the run()
method is to accept messages:
MessageExchange mExchange = channel.accept(TIME_TO_WAIT);
where channel
is an instance of DeliveryChannel
.
When the component receives the message, it extracts the normalized message and then the XML source so that it can analyze the content.
NormalizedMessage msgNor = msg.getInMessage(); Source inSource = msgNor.getContent();
The next step is to extract the content. Our XML message structure is really simple, looking like the clipping shown below:
<sms:smsData xmlns:sms="http://xml.netbeans.org/schema/smsSchema"> <sms:msisdn>recipient_phone_number</sms:msisdn> <sms:body>message_body</sms:body> </sms:smsData>
For more information on the message structure, please refer to smsSchema.xsd. in the sample code. Of course, we can add other tags to better describe our message, but this is enough for this example.
From the XML message, we need to extract the msisdn
and the body
. We can do this using the following piece of code:
NormalizedMessage outMsg = msg.createMessage(); StringBuffer inBuff = XMLHelper.readFromSource(inSource); Document outDoc = XMLHelper. buildDOMDocument(new StringReader(inBuff.toString())); // By now outDoc is equal to the input document String msisdn = XMLHelper.extractData(outDoc, "msisdn"); String body = XMLHelper.extractData(outDoc, "body");
And finally we send the message using our library:
// We have to set the right COM port where the modem is connected SMSSender sender = new SMSSender("COM6"); SMSMessage message = new SMSMessage(); message.setMsisdn(msisdn); // MSISDN is destination number message.setBody(body); // Message Body sender.sendMessage(message);
Installation Phase
Once we have developed our component, we need to describe it so that it can be plugged into the JBI environment. We have to create a XML file descriptor called jbi.xml. In the descriptor, we give a name to our component, and specify the libraries and the .jar name. To make things simple, we'll develop a Service Engine component. It might be more interesting to create a binding component that connects to the GSM modem and a service engine that uses this binding component. In such a case, the service engine could perform some test on the message content and keep track of the messages sent or save the incoming short messages somewhere. This is out of the scope of this article. For now, we collapse everything in one simple component.
<jbi version="1.0" xmlns="http://java.sun.com/xml/ns/jbi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <component type="service-engine"> <identification> <name>SMSService</name> <description>Component to send SMS</description> </identification> <component-class-name>azzola.jbi.sms.SMSComponentImpl</component-class-name> <component-class-path> <path-element>SMSComponent.jar</path-element> <path-element>Simple_Modem_Library.jar</path-element> <path-element>comm.jar</path-element> </component-class-path> <bootstrap-class-name>azzola.jbi.sms.SMSBootstrap</bootstrap-class-name> <bootstrap-class-path> <path-element>SMSComponent.jar</path-element> </bootstrap-class-path> </component> </jbi>
Notice that the classes we developed before are referenced in the component file descriptor.
Now we package everything we created, building a .zip file containing the .jar component file, the libraries, and the jbi.xml file in the META-INF directory. We call this .zip file SMSComponent.zip. Now we can deploy it using NetBeans and GlassFish as the application server and start the component. If we prefer, we can use the GlassFish web interface to do the same tasks. Figure 6 below shows the component installation using the NetBeans interface integrated with GlassFish.
Figure 6. Glassfish installation
Creating an SU and an SA
Once we have installed our component and started it, it is time we exploit it. Before doing this we need to create two XML artifacts: a Service Unit (SU) and the Service Assembly (SA). The Service Unit can't be deployed directly, but it is a part of the Service Assembly. An SU is something like a configuration packaging of a component. A component doesn't do anything until it is configured, and to tell what it should do we need to configure it. Before we described our service using the WSDL language. We can use NetBeans to do it and the file should look like the one one in Figure 7:
Figure 7. WSDL file
The JBI file descriptor contains information extracted from the WSDL file and looks like this:
<?xml version='1.0' encoding="UTF-8" standalone="yes" ?> <jbi version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/jbi" xmlns:sms="http://it.azzola/sms"> <services binding-component="true"> <consumes service-name="sms:smsService" interface-name="sms:smsWSDLOperation" endpoint-name="smsPort"> </consumes> </services> </jbi>
In the jbi.xml file, we specify the interface used to exchange data. This file is a part of the ServiceUnit
.
The next step is to create a directory structure like the one in the figure and then create a new .zip file with the name SMSSU.zip, containing the files shown in Figure 8.
Figure 8. SU structure
Now we can create the service assembly that can be deployed directly in the JBI environment. A service assembly is an aggregation of one or more SU. To do that, we have to create another XML artifact where we specify that we use an HTTP binding component; that is, the interface to our component. A piece of the XML file descriptor is shown here:
<service-unit> <identification> <name>Hello_SoapBC_TestSU1</name> <description> This service unit enables soap inbound endpoint for a service </description> </identification> <target> <artifacts-zip>SMSSU.zip</artifacts-zip> <component-name>sun-http-binding</component-name> </target> </service-unit>
Notice that we refer to the SMSSU.zip we created before, and we use sun-http-binding
to enable the SOAP interface. Now we have to create a directory structure like the one in Figure 9, and finally we create SMSSA.ZIP.
Figure 9. Service Assembly structure
Deploy it and start the application assembly.
Testing the Application
We can test our application. To do so, we can develop a simple web service client to send messages to our application. As an input file we can use a XML file like the one shown below:
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sms="http://xml.netbeans.org/schema/smsSchema"> <soapenv:Body> <sms:smsData> <sms:msisdn>destination number</sms:msisdn> <sms:body>test</sms:body> </sms:smsData> </soapenv:Body> </soapenv:Envelope>
When you run the test class, your mobile phone should ring and notify that you've received a new short message!