XML messaging, Part 1

Write a simple XML message broker for custom XML messages

XML messaging represents a rapidly growing, dynamic area of IT, a situation that makes it exciting and tiresome at the same time. As B2B exchanges and other forms of inter-business electronic communication grow, XML messaging will be more widely deployed than ever.

Read the whole “XML Messaging” series:

  • Part 1: Write a simple XML message broker for custom XML messages
  • Part 2: XML messaging the SOAP way
  • Part 3: JAXM and ebXML set the new standard for XML messaging

In this article, we’ll first explore XML messaging and why it is useful. Then we’ll delve into specific XML messaging features, including message routing, transformation, and brokering. Finally, we’ll finish up with a simple example of an XML broker. After you read and understand the concepts, you should clearly understand which scenarios lend themselves to implementing an XML messaging solution.

What is XML messaging?

To start our exploration, we need to understand the basic premise of XML messaging and what the term messaging implies. For purposes of this article, I define message as follows:

A collection of data fields sent or received together between software applications. A message contains a header (which stores control information about the message) and a payload (the actual content of message).

Messaging uses messages to communicate with different systems to perform some kind of function. We refer to the communication as being message-oriented because we would send and receive messages to perform the operation, in contrast to an RPC (Remote Procedure Call)-oriented communication. A simple analogy may help: think of messaging as email for applications. Indeed, messaging possesses many of the attributes of individuals sending email messages to one another.

In the past, when you were using or working on a message-oriented system, it meant that you were using some kind of MOM (message-oriented middleware) product like Tibco’s Rendezvous, IBM’s MQSeries, or a JMS provider to send messages in an asynchronous (one-way) fashion. Messaging today doesn’t necessarily mean that you are using a MOM product, and it doesn’t necessarily mean that you are communicating asynchronously. Rather, messaging can be either synchronous (two-way) or asynchronous and use many different protocols such as HTTP or SMTP, as well as MOM products.

Why XML messaging?

Why would you want to develop a system using messaging? What makes messaging a useful design technique and what are the benefits? As mentioned earlier, we can chose from two alternatives when requiring two applications to talk to each other over a network: RPC or message-oriented. Using an RPC-based approach (RMI falls into this category) means that the client (or caller) of the procedure call knows the procedure it wants to invoke and knows the parameters it wishes to pass to the procedure. Also, the caller wishes to wait while the called server (the application) completes the request.

In the second approach — message-oriented — the caller does not necessarily know the exact procedure that will be invoked, but instead creates a message of a specific format known to both the client and the server. The client creates the message and then sends it to the server over the network. Therefore, the client does not depend on the server or the server’s procedures, but is dependent on the message format. Also, the communication likely takes place in an asynchronous fashion, meaning that the client will send off a request but will not wait (block) for the response. This enables the client to continue to function even if the server becomes unavailable (crashes, for example). This design, where the client is more independent of the server, is considered to be more loosely coupled.

When evaluating whether to use a message-oriented design it is important to understand the pros and cons of such a system. The pros include:

  • Loose coupling
  • Easier message routing and transformation
  • More flexible payload (can include binary attachments, for example)
  • Can use several messages together to invoke a given procedure

In general, a message-oriented approach proves more flexible than an RPC approach.

Now here are some cons:

  • It requires more work to develop a client/server interaction using a message-oriented approach compared with an RPC approach like RMI because developing a client/server interaction via a message represents another level of indirection from RPC. Complexity is added through the creation of the message on the client side (versus a procedure invocation in an RPC approach) and on the server side with message-processing code. Because of its added complexity, a message-oriented design can be more difficult to understand and debug.
  • There is a risk of losing type information for the programming language in which the message was sent. For example, double in Java may not translate as a double in the message.
  • In most cases the transactional context in which the message was created does not propagate to the messaging server.

Considering the pros and cons, when should you use a message-oriented approach? The most common scenario occurs when the client/server communication takes place over the Internet and the client and server belong to different companies. In this scenario it could be fairly difficult to have the two companies agree on the procedure interface. Also, it’s possible that the companies might not want to use the same programming language. In another example, the companies involved may want to use an asynchronous communication model so that neither will depend on the other’s application being up and running.

Another attractive messaging scenario occurs when you’re developing an event-based system in which events are created and then consumed by interested parties. Most GUIs are event-based. For instance, they might create a mouse click event in which interested parties listen for the event and perform some action based on it. In this scenario, using a messaging approach allows you to remove the dependency between an event (or action in a system) and the system’s reaction to the event that is performed on the server.

Now that we understand a bit about messaging, we’re ready to add XML to the equation. The addition of XML to messaging means that we are able to make use of a flexible data formatting language for our messages. In messaging, both the client and the server need to agree on a message format. XML makes this easier by deciding many data formatting issues and with the addition of other XML standards such as Rosetta Net. No additional work is required to come up with a message format.

What does an XML message broker do?

A message broker acts as the server in a message-oriented system. Message broker software performs operations on messages it receives. These operations include:

  • Header processing
  • Security checks and encryption/decryption
  • Error and exception handling
  • Routing
  • Invocation
  • Transformation

Header processing

Header processing is usually one of the first functions performed in the message upon its receipt within an XML broker. Header processing involves examining the header fields of incoming messages and performing some functions. Header processing could include adding a tracking number to an incoming message or ensuring that all of the header fields necessary to process the message exist. In the example XML message below, the message broker could check the to header field to ensure that this is the proper destination for this message.

Security checks and encryption/decryption

From a security perspective, a message broker can perform several different operations, but most likely you’ll want to accomplish the “big three” of security: authentication, authorization, and encryption. First, once it determines that the message contains data that can be used to authenticate, the message broker will authenticate messages against a security database or directory. Second, the message broker will authorize operations that can be performed with this type of message and an authorized principal. Finally, the message that arrives at the message broker may be encrypted using some encryption scheme. It will be the broker’s responsibility to decrypt the message in order to process it further.

Error and exception handling

Error and exception handling is another important piece of functionality performed by the message broker. Generally, the message will respond to the client (assuming a synchronous invocation) with an error message, caused when the message sent to the broker does not contain sufficient or accurate information. Another cause for errors or exceptions would occur when servicing the request (actually invoking a procedure/method based on the payload of the message).

Routing

Message routing is branching logic for messages. It occurs at two different levels in a message. The first, header-level routing, determines if an incoming message is bound for this application or needs to be resent to another application. The second, payload routing, determines which procedure or method to invoke once the broker determines that the message is bound for this application. Together these two types of routing enable a rich set of functionality when dealing with messages.

Invocation

Invocation means to actually call or invoke a method with the data (payload) contained in the incoming message. This could produce a result that is then returned through the broker back to the client. What is invoked could be anything, including an EJB Session bean, class method, and so on.

Transformation

Transformation converts or maps the message to some other format. With XML, XSLT is commonly used to perform transformation functionality.

An example XML message

Below you’ll find an XML message used in the sample application that follows. Notice the header and body portions. This example is a “saveInvoice” type of message, in which the body contains an invoice that needs to be saved.

<?xml version="1.0"?>
<message>
   <header>
      <to>companyReceiver</to>
      <from>companySender</from>
      <type>saveInvoice</type>
   </header>
   <body>
     <saveInvoice>
      <invoice date="01-20-2000" number="123">
          <address country="US">
         <name>John Smith</name>
         <street>123 George St.</street>
         <city>Mountain View</city>
         <state>CA</state>
         <zip>94041</zip>
          </address>
          <billTo country="US">
         <name>Company A</name>
         <street>100 Main St.</street>
         <city>Washington</city>
         <state>DC</state>
         <zip>20015</zip>
          </billTo>
          <items>
         <item number="1">
             <name>IBM A20 Laptop</name>
             <quantity>1</quantity>
             <USPrice>2000.00</USPrice>
         </item>
          </items>
      </invoice>
       </saveInvoice>
   </body>
</message>

You may wonder whether there is an advantage to developing a custom XML message. Why not use one of the existing message standards like ebXML or SOAP to encapsulate the payload (the invoice)? There are a couple of reasons. The first is to demonstrate some of the contents needed in a message without all of the complexity of explaining a full-blown industry standard. Second, although the existing standards fill most needs, there still will be scenarios in which using a custom message will better fit the needs of a situation, much like the tradeoffs between using a higher-level protocol like HTTP or SMTP and using raw sockets.

A prototype XML broker implementation

Having discussed the reasons for using a messaging design in your application, we will now proceed to a prototype XML messaging broker implementation.

Why should you develop a custom message broker implementation instead of using an existing one? Well, because many of the implementations for XML messaging products are new, so it’s important to know what goes into a basic implementation. Also, it’s possible that because XML message brokers are somewhat immature products, you’ll need to develop your own to get the features you want.

The basic broker presented here can service two types of messages: a request to create an invoice, which it stores on the filesystem, and a client code component, which just reads the XML message from a file and sends it.

The broker comprises three main parts: a listener piece that receives incoming messages on some transport (in this example there will only be an HTTP implementation provided); the main broker piece, which will decide what to do with an incoming message; and the invocation piece that will actually perform some logic based on the incoming message. Let’s look at each in more detail.

Receive the message from the transport

A message will first encounter the broker’s listener portion. Most XML message brokers provide support for many different transports (protocols) such as HTTP, SMTP, JMS (a particular vendor’s implementation), and so on. Our broker allows for this by isolating the transport portion. The piece shown below simply receives the message on a given transport, places the incoming message into a string variable, and calls the broker singleton:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ServletListener extends HttpServlet {
    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        response.setContentType("text/plain");
        InputStream in = request.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
      StringBuffer sb = new StringBuffer();
      String line = reader.readLine();
      while (line != null) {
         sb.append(line);
         line = reader.readLine();
      }
      BrokerEngine brokerEngine = BrokerEngine.getInstance();
      out.println(brokerEngine.serviceSync(sb.toString()));
    }
}

Now, on to the broker engine.

The broker engine

The core broker, which makes the decisions, is called by one of the listeners with the message. At this point it parses the message into a DOM and decides which invoker class to call based on the contents of the message:

import java.io.*;
import java.util.*;
import org.apache.xerces.parsers.*;
import org.apache.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import org.apache.xml.serialize.*;
public class BrokerEngine {
    private static BrokerEngine instance = null;
    private Map serviceMap;
    private BrokerEngine() {
      serviceMap = new HashMap();
      serviceMap.put("saveInvoice", new SaveInvoiceInvoker());
    }
    public static synchronized BrokerEngine getInstance()
    {
      if(instance == null)
           instance = new BrokerEngine();
        return instance;
    }
    public String serviceSync (String strRequest) {
      InputSource in = new InputSource(new StringReader(strRequest));
      DOMParser parser = new DOMParser();
      try {
         parser.parse(in);
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      Document request = parser.getDocument();
      //invoke a method/procedure
      Document response = invoke(request);
      StringWriter strResponse = null;
      try {
         OutputFormat format  = new OutputFormat(response);
         strResponse = new StringWriter();
         XMLSerializer serial = new XMLSerializer( strResponse, format );
         serial.asDOMSerializer();
         serial.serialize(response.getDocumentElement());
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      return strResponse.toString();
   }
    public void serviceAsync (String request) {
      InputSource in = new InputSource(new StringReader(request));
      DOMParser parser = new DOMParser();
      try {
         parser.parse(in);
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      Document doc = parser.getDocument();
      //invoke a method/procedure
      invoke(doc);
   }
   private Document invoke(Document doc) {
      String type = null;
      try {
           Node node = XPathAPI.selectSingleNode(doc, "/message/header/type/text()");
           type = node.getNodeValue();
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      Invoker invoker = (Invoker) serviceMap.get(type);
      Document response = invoker.invoke(doc);
      return response;
   }
   //test case
    public static void main (String[] args) {
      if (args.length < 1) {
         System.out.println("Usage: BrokerEngine <message>");
         System.exit(-1);
      }
      StringBuffer sb = new StringBuffer();
      try {
         RandomAccessFile randFile = new RandomAccessFile(args[0], "r");
         String line = randFile.readLine();
         while (line != null) {
            sb.append(line);
            line = randFile.readLine();
         }
         randFile.close();
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      BrokerEngine brokerEngine = BrokerEngine.getInstance();
      System.out.println("result: n" + brokerEngine.serviceSync(sb.toString()));
   }
}

The invoker interface

An invoker, a concrete implementation class, services a particular message type. In this example implementation, there is only one invoker: SaveInvoiceInvoker. Keep in mind that each concrete invoker must implement the invoker interface. The interface proves fairly simple, taking the incoming message in as a parameter and returning the response message:

import org.w3c.dom.*;
public interface Invoker {
   Document invoke (Document doc);
}

Now let’s look at the implementation class that actually stores the invoice on the filesystem. First a few fields are extracted, then the invoice is saved to the filesystem, and finally a return message is created.

import java.io.*;
import org.apache.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import org.apache.xml.serialize.*;
public class SaveInvoiceInvoker implements Invoker {
   public Document invoke (Document doc) {
      String strTo = null, strFrom = null, strType = null;
      try {
           Node toNode = XPathAPI.selectSingleNode(doc, "/message/header/to/text()");
           strTo = toNode.getNodeValue();
           Node fromNode = XPathAPI.selectSingleNode(doc, "/message/header/from/text()");
           strFrom = fromNode.getNodeValue();
           Node typeNode = XPathAPI.selectSingleNode(doc, "/message/header/type/text()");
           strType = typeNode.getNodeValue();
           Node numberNode = XPathAPI.selectSingleNode(doc, "/message/body/saveInvoice/invoice/@number");
           String invoiceNumber = numberNode.getNodeValue();
           Node invoiceNode = XPathAPI.selectSingleNode(doc, "/message/body/saveInvoice/invoice");
           OutputFormat format  = new OutputFormat(doc);
           StringWriter strInvoice = new StringWriter();
           XMLSerializer serial = new XMLSerializer( strInvoice, format );
           serial.asDOMSerializer();
           serial.serialize((Element)invoiceNode);
           String filename = invoiceNumber + ".xml";
           File file = new File(filename);
           if (!file.exists()) {
             RandomAccessFile randFile = new RandomAccessFile(file, "rw");
             randFile.writeBytes(strInvoice.toString());
             randFile.close();
           }
      }
      catch (Exception e) {
         e.printStackTrace();
         return MessageFactory.createReturnMessage(strFrom,strTo, strType, "Error processing");
      }
      return MessageFactory.createReturnMessage(strFrom,strTo, strType, "Success!");
   }
}

Currently this design requires you to create an invoker class implementation for each method you wish to invoke. Although this is the most flexible approach as far as the contents of the message, it could be fairly tedious if there are hundreds of messages. In another option for designing these invoker classes, you could have one invoker class for each type of method invoked (for example, an EJB invoker class). The reflection-based approach, which uses Java reflection to call the specific method, has pros and cons. One pro: it frees you from having to create an invoker class implementation for each method you wish to invoke. On the other hand, the message and method it will call are much more tightly coupled.

The MessageFactory utility class

I also include a MessageFactory utility class that generates the message responses using a DOM data structure:

import java.io.*;
import org.w3c.dom.*;
import  org.apache.xerces.dom.*;
import  org.apache.xml.serialize.*;
public class MessageFactory
{
   public static Document createReturnMessage(String strTo,
                                     String strFrom,
                                        String strType,
                                     String strResult) {
      Document doc = new DocumentImpl();
      Element message = doc.createElement("message");
      Element header = doc.createElement("header");
      Element to = doc.createElement("to");
      header.appendChild(to);
      to.appendChild( doc.createTextNode(strTo));
      Element from = doc.createElement("from");
      header.appendChild(from);
      from.appendChild( doc.createTextNode(strFrom));
      Element type = doc.createElement("type");
      header.appendChild(type);
      type.appendChild( doc.createTextNode(strType));
      message.appendChild(header);
      Element body = doc.createElement("body");
      Element result = doc.createElement("result");
      body.appendChild(result);
      result.appendChild( doc.createTextNode(strResult));
      message.appendChild(body);
      doc.appendChild(message);
      return doc;
    }
}

Now, having covered the prototype broker in detail, we will reflect on the implementation.

Some points about the current broker implementation

It’s important to note the data structures that are used in this design and in other message broker implementations to encapsulate messages while inside the broker. Messages in this implementation for the most part use a DOM or string data structure to represent a message. Other alternatives for message encapsulation include the XML/Java binding or JDOM.

The current design exhibits some problems. To wit, three main cases exist for incoming messages: one-way message (only inbound, no return message), request/reply synchronous (the reply is sent back over the same connection by which the request arrived), and request/reply asynchronous (the reply is sent via a different connection to client). The current implementation does not handle the third case at all, because it does not have knowledge of where to send the return message.

Conclusion

This article has given you a basic introduction to XML messaging and has examined a brief message broker implementation. Keep in mind that the power of XML messaging makes it increasingly pervasive in software development. So whether you’re developing a simple online invoicing system or a large-scale B2B exchange, or if you’re simply an end user to one of these systems, it’s likely that XML messaging will play a crucial role.

In Part 2, I’ll introduce XML messaging with SOAP and show you a basic example using it.

Dirk Reinshagen, an independent consultant in
the San Francisco Bay area, has more than eight years of software
architecture and development experience. He holds a B.S. in
computer science from Tufts University.

Source: www.infoworld.com