JaVa
   

Messaging in Java

JMS

We have just discussed MOMs and the kinds of apps that may require their use. Java Message Service (JMS) provides Java apps with a standard and consistent interface to the messaging services of a MOM provider or a messaging server. This benefits architects, because they no longer need to use the messaging vendor's proprietary Java interface, if one exists at all. JMS-based communication is a potential solution in any distributed computing scenario that needs to pass data, not references, either synchronously or asynchronously between app components. It offers a good solution for business integration in heterogeneous environments and for connecting to legacy systems. For example, an enterprise computing strategy can use JMS-based distributed software components as a middleware solution that functions as a bridge among legacy apps. Architecturally, the JMS stack revolves around the concept of providers. This provides the underlying queuing and guaranteed delivery, ensuring that "offline" apps get the messages later, when they are capable of receiving them. Screenshot shows how message delivery and acknowledgment works for a durable message consumer and how the provider acts as a proxy that actually receives messages on behalf of the eventual recipients.

Java Click To expand
Screenshot: Conceptual model for JMS Java Click To expand
Screenshota: Point-to-point messaging

From a messaging perspective, JMS supports two messaging models:

Without going into too much detail about JMS, we will discuss messaging with it and how XML/SOAP messages can be delivered with it. The JMS implementations of point-to-point and publish-subscribe paradigms use the same fundamental concepts but have specialized classes to handle them. In simple terms, there is a common set of base interfaces. Each base interface has at least two sub-interfaces: one for the point-to-point model, and the other for the publish-subscribe model. Screenshot shows the core of the JMS API.

Java Click To expand
Screenshot: The JMS API

Destination

Destination represents an abstraction of a message delivery or endpoint. The provider uses this interface to define the location where messages are delivered The provider is, of course, free to choose any underlying implementation mechanism. Clients use the sub-interfaces javax.jms.Queue and javax.jms.Topic, depending on the message style.

Connection Factory

ConnectionFactory is an abstraction used to encapsulate the specifics of connecting to a JMS provider from an app. The task of a connection factory is to create a provider-specific connection to the JMS provider. This is similar to the way the driver manager (java.sql.DriverManager) works in JDBC. The app programmer needs only to get the database-specific driver, which returns a connection to the database.

Java Start Sidebar

In JMS, the term administered object refers to an object the app program retrieves from the JNDI context and works with as though it were a local object. The app administrator configure these objects in the app setup. The closest analogy is configuring the long-distance carrier on a telephone line. The phone company can configure the administered "carrier" object as MCI or AT&T. To the client, the calls work the same, without the need to buy a new telephone every time the calling plan or carrier changes. Destination and ConnectionFactory are administered objects.

Java End Sidebar

Connection

An app uses ConnectionFactory to create a connection to the JMS provider. The connection is a network connection of some type (a socket, RMI reference, etc., depending on how the provider implements it) and represents a single communication channel to the provider.

Sessions

Once a Connection is obtained to a provider, a Session is started, and all activity takes place in the context of the Session. A Session represents a conversation or collection of transactional interactions with the underlying provider.

Message Producers

To send a message to a Destination, a client must ask the Session to create a MessageProducer. For point-to-point messaging, a javax.jms.QueueSender is created, and for publish-subscribe messaging, a javax.jms.Topicuploader is created.

Message Consumers

Message consumers are created by the Session for clients that want to receive messages. Message consumers are attached to a Destination and, depending on the messaging style, the javax.jms.QueueReciever or javax.jms.TopicSubscriber is used. The client can attach javax.jms.MessageListener with the consumer, with which the callback method onMessage() is invoked asynchronously when a message arrives. We have taken a high-level view of the different parts of the JMS API. The basic steps, as summarized in Screenshot, are:

Java Click To expand
Screenshot: JMS messaging
  1. Obtain the ConnectionFactory from JNDI.

  2. Obtain the Destination from JNDI.
  3. Use the connection factory to create a Connection.
  4. Use the connection to start a Session.
  5. Use the Session and Destination to create MessageConsumers and MessageProducers as necessary and attach a MessageListeners.
  6. Send messages with the producer and receive messages with the consumer.

For app A to send messages to app B with JMS, using point-to-point messaging (Screenshot), the code would look like Listings 11.1 and 11.2.

Java Click To expand
Screenshot: Sending messages with point-to-point messaging Listing 11.1: app A: Sending the message
String message="Some message here ";
// JMS factories and queues are administered objects.
String JNDIFACTORY="weblogic.jndi.WLInitialContextFactory";
String JMSFACTORY ="myQueueFactory";
String Q ;
String endpoint= "t3://192.168.0.1:9090";
Hashtable env = new Hashtable();
env.put(Context.INITIALCONTEXTFACTORY, JNDIFACTORY);
env.put(Context.PROVIDERURL, endpoint);
InitialContext ctx = new InitialContext(env);
QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup(JMSFACTORY);
QueueConnection connection = factory.createQueueConnection ();
connection.start();
QueueSession session =
 connection.createQueueSession(false,QueueSession.AUTOACKNOWLEDGE);
Queue queue = (Queue) ctx.lookup(QNAME);
QueueSender queuesender =session.createSender(queue);
TextMessage message = session.createTextMessage();
message.setText(MESSAGE);
queuesender.send(message);
ctx.close();
session.close();
connection.close();




Java End example
Listing 11.2: app B: Receiving the message
String JNDIFACTORY="weblogic.jndi.WLInitialContextFactory";
// JMS factories and queues are adminsitered objects.
String JMSFACTORY="myQueueFactory";
String Q ;
String endpoint= "t3://192.168.0.1:9090";
Hashtable env = new Hashtable();
env.put(Context.INITIALCONTEXTFACTORY, JNDIFACTORY);
env.put(Context.PROVIDERURL,endpoint);
InitialContext ctx = new InitialContext(env);
QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup(JMSFACTORY);
QueueConnection connection = factory.createQueueConnection ();
connection.start();
QueueSession session =
 connection.createQueueSession(false,QueueSession.AUTOACKNOWLEDGE);
Queue queue = (Queue) ctx.lookup(QNAME);
QueueReceiver receiver = session.createReceiver(queue);
receiver.setMessageListener (new MyListener(){
 public void onMessage(Message msg) {
 String msgText;
 if (msg instanceof TextMessage) {
 msgText = ((TextMessage)msg).getText();
 } else{
 msgText = msg.toString();
 }
 System.out.println("Message Received: "+ msgText);
 }
 });
ctx.close();
session.close();
connection.close();



Java End example

The model shown in Listings 11.1 and 11.2 is quite robust and works well for enterprise-level messaging. It supports a variety of message types (e.g., BytesMessage, MapMessage, ObjectMessage, StreamMessage, and TextMessage), of which the above code fragment demonstrates the latter. JMS can also easily be adapted for XML messaging. The text message shown above could be replaced with an XML document and sent from app A to app B. However, JMS is just a specification. It does not define the wire-level protocol or encodings the provider uses to implement the messaging. For example, in Listings 11.1 and 11.2, provider_url points to "t3://192.168.0.1:9090", showing that the underlying implementation uses a t3 protocol or BEA's implementation of RMI to connect to the provider. In this case, if the client were anything other than BEA, to receive the messages it would need BEA's API or client files, containing the factories and implementation classes, to connect to BEA's provider. JMS vendors often provide the ability to extend themselves across firewall boundaries, using some form of HTTP tunneling. However as described above, it still requires a slice of the JMS vendor's software to be installed at every remote location. apps within the enterprise boundaries usually control every remote node involved in the communication, which is a perfectly viable solution. Unfortunately, this is not usually the case for interenterprise communications. Many vendors have enhanced their JMS provider implementations to support SOAP messaging over HTTP. They use HTTP as the transport under the JMS API and pass SOAP messages as the JMS TextMessage (this is often referred to as SOAP over JMS). The message format used to transport data over HTTP is vendor-proprietary, usually some form of multipart MIME. This is quite a robust messaging approach, but the problem remains that JMS was not really intended for this purpose. For example, there is no way to create and manipulate a SOAP message envelope with an attachment in JMS. Developers would have to use complicated, error-prone String operations or the provider's proprietary API. Some JMS vendors have taken this approach one step further, by using SOAP with Attachments API for Java (SAAJ) to create and manipulate messages, HTTP transport, and a SOAP message (instead of an HTTP multipart message). This is also a viable XML messaging option, with the same functionality as a JAXM provider. The drawback is vendor lock-in, because the wire-level SOAP message format and endpoint are vendor-specific. Our reason for covering JMS above is that essentially JAXM has its roots in JMS. The close similarity between the two will be more apparent further in the chapter. For example, JMS and JAXM both have a ConnectionFactory, a MessageListener interface, and so on.

JavaMail

JMS is the preferred technology for integration and messaging between loosely coupled Java apps. While JMS is a specification that creates an abstraction at the app level, as opposed to the wire protocol, JavaMail can be thought of as an object-oriented wrapper around the standard messaging protocols—SMTP, POP3, and IMAP. SMTP is used to send email, and POP3 and IMAP are used to retrieve email. In this section, we will look at an alternative messaging mechanism—JavaMail—and, more important, how SOAP can be used with JavaMail for asynchronous XML messaging between apps.

Java Start Sidebar

The Simple Mail Transfer Protocol (SMTP) specified in RFC 821 (www.ietf.org/rfc/rfc821.txt) defines the mechanism for delivery of email to a SMTP server. The server relays the message to the recipients' SMTP server, from which users download the mail using POP (Post Office Protocol) or IMAP (Internet Message Access Protocol). POP and IMAP are defined by RFC 1939 (www.ietf.org/rfc/rfc1939.txt) and RFC 2060 (www.ietf.org/rfc/rfc2060.txt), respectively.

Java End Sidebar

Email apps can be divided into two broad categories: Mail User Agent (MUA) and Mail Transfer Agent (MTA) apps. MUA apps, such as Eudora, Netscape Messenger, and Microsoft Outlook, allow messages to be composed, accessed, and sent. MTA apps, such as iPlanet Messaging server and Microsoft Exchange, handle the actual physical delivery. JavaMail, shown in Screenshot, is a standard extension API focused on building MUA functionality into Java apps. Like JMS, it is designed with provider-based architecture, for wire protocol independence. Several MTAs are freely available, including "JAMES" from Apache, which uses POP3/SMTP protocols. It can be found at http://jakarta.apache.org/james/index.html and is included in the CD for this tutorial.

Java Click To expand
Screenshot: Conceptual JavaMail model

The JavaMail API, shown in Screenshot, consist of three packages that contain both the client API and the API that serves as a contract for providers to implement. These packages also contain the classes and interfaces to model events, notifications, and searching. We will not discuss all these features in detail but will limit our discussion to sending and receiving messages.

Java Click To expand
Screenshot: The JavaMail API

Screenshot illustrates the concept behind sending mail messages using the JavaMail API. A Session is created to a server, using an underlying Provider. Message objects are created and sent using the Transport for that provider session. The semantics of how a provider establishes a session and the physical connection are completely abstracted from the app code.

Java Click To expand
Screenshot: Sending mail using JavaMail

The Message is logically composed of different sections, as Figures 11.15a and 11.15b illustrate. It has some Header attributes (To, From, Subject, etc.) and the content data. Email messages that contain attachments are modeled as Multipart messages. In a Multipart message, the body consists of many BodyPart objects.

Java Click To expand
Screenshot: Message structure for (a) simple messages and (b) multipart messages

Let us now look at an example of asynchronous messaging, in which Flute Bank sends a purchase order to OfficeMin, using SOAP. To send a SOAP message, we will follow the steps outlined in Screenshot. We will replace the body of the mail message with the SOAP envelope structure and attach the XML file that represents the purchase order. Listing 11.3 shows the code for sending this mail from purchaseorders@bugmenot.com to invoices@bugmenot.com. The JavaMail API comes with providers from Sun for POP3, SMTP, and IMAP, so no vendor products (such as MOM implementations) are needed to run this example.

Listing 11.3: The SoapMailSender app
package com.flutebank.javamail import java.util.*;
import java.io.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
/**
 * SoapMailSender creates a message with the SOAP envelope as the body and the
 * business document exchanged between companies as the attachment.
 */
public class SoapMailSender {
 private static String messageText1 =
 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
 "<soap-env:Envelope xmlns:soap-env=
 \"http://schemas.xmlsoap.org/soap/envelope/\">" +
 "<soap-env:Header/>" +
 "<soap-env:Body>" +
 "<po:PurchaseOrder xmlns:po=\"http://www.flutebank.com/schema\">"+
 "<senderid>myuserid@Mon Aug 19 23:55:28 EDT 2002</senderid>"+
 "</po:PurchaseOrder>"+
 "</soap-env:Body>"+
 "</soap-env:Envelope>";
 public static void main(String[] args) {
 try {
 String to = args[0];
 String from = args[1];
 String host = args[2];
 boolean debug = Boolean.valueOf(args[3]).booleanValue();
// create some properties and get the default Session
 Properties props = new Properties();
 props.put("mail.smtp.host", host);
 Session session = Session.getDefaultInstance(props, null);
// create a message
 MimeMessage message = new MimeMessage(session);
// create the from
 message.setFrom(new InternetAddress(from));
// create the recipient headers
 InternetAddress[] address = {new InternetAddress(to)};
 message.setRecipients(Message.RecipientType.TO, address);
// create the subject and date header
 message.setSubject("PurchaseOrder");
 message.setSentDate(new Date());
// create and fill the first message part
 MimeBodyPart mbp1 = new MimeBodyPart();
 mbp1.setText(messageText1);
// create and fill the second message part
 MimeBodyPart attachment = new MimeBodyPart();
// attach the purchaseorder.xml file to the message
 FileDataSource fds= new FileDataSource("purchaseorder.xml");
 attachment.setDataHandler(new DataHandler(fds));
 attachment.setFileName("purchaseorder.xml");
// create the Multipart and its parts
 Multipart mp = new MimeMultipart();
 mp.addBodyPart(mbp1);
 mp.addBodyPart(attachment);
// add the Multipart to the message
 message.setContent(mp);
// send the message
 Transport.send(message);
 } catch (Exception mex) {
 mex.printStackTrace();
 }
 }
}



Java End example

The mail message can be retrieved at officemin.com. Screenshot shows the minimum steps required to retrieve mail using an underlying provider. A Session object is used to obtain a Store created for the POP3 protocol. The Store is connected to a server by a username and password, and the user's Folder is opened. Messages in the Folder are then accessed and downloaded.

Java Click To expand
Screenshot: Retrieving mail using JavaMail

Listing 11.4 shows the code on OfficeMin's side to access the POP3 server and download all messages with JavaMail. The code dumps the message headers, content, and attachments to the console.

Listing 11.4: The SoapMailReceiver app
package com.officemin.purchaseorderservice;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
import java.io.*;
/**
 * A Receives a mail message and prints out the contents
 */
 public class SoapMailReceiver{
 public static void main(String args[]) throws Exception {
 String server=args[0];
 String username=args[1];
 String password=args[2];
// get the default session
 Properties props = System.getProperties();
 Session session = Session.getDefaultInstance(props);
// get the POP3 message store and connect to it
 Store store = session.getStore("pop3");
 store.connect(server, username, password);
// get the default folder in the store
 Folder folder = store.getDefaultFolder();
// get the INBOX folder in the default folder
 folder = folder.getFolder("INBOX");
// download the message or leave it on the server.
// we leave it on the server by using read-only
 folder.open(Folder.READ_ONLY);
// Get the messages
 Message[] msgs = folder.getMessages();
// process the messages
 for (int msgNum = 0; msgNum < msgs.length; msgNum++){
 // insert business logic here to process the message
 // for now we just print it out
 processMessage(msgs[msgNum]);
 }
// close everything
 folder.close(false);
 store.close();
 }
/**
 * Dump the message contents to the console
 */
public static void processMessage(Message message){
 try{
// get the header information
 String from=((InternetAddress)message.getFrom()[0]).getPersonal();
 if (from==null)
 from=((InternetAddress)message.getFrom()[0]).getAddress();
 System.out.println("FROM: "+from);
 String subject=message.getSubject();
 System.out.println("SUBJECT: "+subject);
// get the message part (i.e., the message itself)
 Part messagePart=message;
 Object content=messagePart.getContent();
 for(int i=0;i < ((Multipart)content).getCount(); i++){
 messagePart=((Multipart)content).getBodyPart(i);
 InputStream is = messagePart.getInputStream();
 BufferedReader reader =new BufferedReader(new InputStreamReader(is));
 String thisLine=reader.readLine();
 while (thisLine!=null) {
 System.out.println(thisLine);
 thisLine=reader.readLine();
 }
 }
}catch (Exception ex){
 ex.printStackTrace();
 }
 }
}



Java End example

The simple JavaMail API can be used to build complex messaging apps. Consider the business use case of flutebank.com and officemin.com discussed earlier. Flute Bank places a regular order for its main branch with OfficeMin, by sending a purchase order. OfficeMin processes the purchase order and ships the supplies. OfficeMin then sends an invoice to Flute Bank that Flute's accounting processes. Flute then sends an electronic payment to OfficeMin. Screenshot shows how this can be realized:

  1. The client app initiates the business exchange by sending a SOAP message with the purchase order as an XML attachment, using SMTP. The sender and recipient are indicated by the from and to email headers. Flute's local mail server acts as a sort of messaging provider and stores the message, which is reliably delivered (possibly with a delivery receipt) to officemin.com. We looked at this code in Listing 11.3.

  2. The PurchaseOrderService at OfficeMin either picks up the mail or is notified when it arrives. It downloads the mail and processes the headers and the body, which contains a SOAP message. It also extracts the attachment, which contains the order, and processes it. We looked at this code in Listing 11.4.
  3. The SendInvoice app is invoked, which now acts like a client and sends the invoice to Flute Bank, as in step 1.
  4. Flute's AccountingService picks up the message containing the invoice, as in step 2, and processes the payment to OfficeMin.
Java Click To expand
Screenshot: Asynchronous B2B messaging using JavaMail

The above scenario shows how an asynchronous message-exchange scenario can be built using JavaMail and XML technologies. It is not necessary that Java be used on both sides. For example, OfficeMin could use Visual Basic or C++ to download email from its servers. Messaging with JavaMail—and in particular, SOAP messaging with JavaMail— offers several advantages:

Java Start Sidebar

While an email is an asynchronous one-way message exchange, SMTP offers a delivery notification mechanism called Delivery Status Notification (DSN) and a Message Disposition Notification (MDN), in which a receipt email message is sent back.

Java End Sidebar

Using JavaMail for enterprise messaging and integration does have some limitations:

  • No transaction support. Although mail-based messaging solutions use tried-and-tested technology, they do no support transactions across enterprise boundaries. Most architects interpret this as an absolute design constraint for any adoption. Without a stringent requirement on transactions across enterprise boundaries, architectures such as those outlined in Screenshot can be a good fit. If is this is not the case, see for details on transactions in Web services.

  • Repeated delivery of the same message. Just as receiving an email in your mailbox twice is a common occurrence, the same message may be delivered to apps multiple times. In short, a mail based solution offers no guaranteed one-time delivery support . The app layer must incorporate the logic of filtering this out if such an event would affect the business process. For example, receiving the same purchase order twice can trigger two shipments, but receiving the same free stock quote twice may not be so critical an issue.

JaVa
Comments