Messaging in Java


JMS

We have just discussed MOMs and the kinds of applications that may require their use. Java Message Service (JMS) provides Java applications 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 application 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 applications.

Architecturally, the JMS stack revolves around the concept of providers. This provides the underlying queuing and guaranteed delivery, ensuring that "offline" applications get the messages later, when they are capable of receiving them. Figure 11.7 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.

click to expand
Figure 11.7: Conceptual model for JMS

click to expand
Figure 11.8a: Point-to-point messaging

From a messaging perspective, JMS supports two messaging models:

  • Point-to-point. In this model, the sender sends the message, which is put on a queue, and a single consumer reads the message off the queue. There is exactly one recipient (Figure 11.8a).

  • Publish-subscribe. In this model, the sender sends the message, which is put on a topic. Multiple recipients can subscribe to the topic; each receives a copy of the message (Figure 11.8b).

    click to expand
    Figure 11.8b: Publish-subscribe messaging

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. Figure 11.9 shows the core of the JMS API.

click to expand
Figure 11.9: 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 application. 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 application programmer needs only to get the database-specific driver, which returns a connection to the database.

start sidebar

In JMS, the term administered object refers to an object the application program retrieves from the JNDI context and works with as though it were a local object. The application administrator configure these objects in the application 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.

end sidebar

Connection

An application 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.TopicPublisher 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 Figure 11.10, are:

click to expand
Figure 11.10: 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 application A to send messages to Application B with JMS, using point-to-point messaging (Figure 11.11), the code would look like Listings 11.1 and 11.2.

click to expand
Figure 11.11: Sending messages with point-to-point messaging

Listing 11.1: Application A: Sending the message

start example
 String message="Some message here "; // JMS factories and queues are administered objects. String JNDIFACTORY="weblogic.jndi.WLInitialContextFactory"; String JMSFACTORY ="myQueueFactory"; String QNAME="mymessagequeue"; 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(); 
end example

Listing 11.2: Application B: Receiving the message

start example
 String JNDIFACTORY="weblogic.jndi.WLInitialContextFactory"; // JMS factories and queues are adminsitered objects. String JMSFACTORY="myQueueFactory"; String QNAME="mymessagequeue"; 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(); 
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 application A to application 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. Applications 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 applications. While JMS is a specification that creates an abstraction at the application 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 applications.

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.

end sidebar

Email applications can be divided into two broad categories: Mail User Agent (MUA) and Mail Transfer Agent (MTA) applications. MUA applications, such as Eudora, Netscape Messenger, and Microsoft Outlook, allow messages to be composed, accessed, and sent. MTA applications, such as iPlanet Messaging server and Microsoft Exchange, handle the actual physical delivery. JavaMail, shown in Figure 11.12, is a standard extension API focused on building MUA functionality into Java applications. 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 book.

click to expand
Figure 11.12: Conceptual JavaMail model

The JavaMail API, shown in Figure 11.13, 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.

click to expand
Figure 11.13: The JavaMail API

Figure 11.14 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 application code.

click to expand
Figure 11.14: 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.

click to expand
Figure 11.15: 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 Figure 11.14. 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@flutebank.com to invoices@officemin.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 application

start example
 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();         }     } } 
end example

The mail message can be retrieved at officemin.com. Figure 11.16 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.

click to expand
Figure 11.16: 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 application

start example
 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();     }   } } 
end example

The simple JavaMail API can be used to build complex messaging applications. 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. Figure 11.17 shows how this can be realized:

  1. The client application 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 application 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.

click to expand
Figure 11.17: 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:

  • Eliminates messaging MOMs. The sender and receiver do not need any JMS infrastructure or vendor software. This is especially significant for non-Java environments.

  • Supports security. Two broad concerns affect most email applications: authentication and encryption. Basic authentication is built into email, and a majority of enterprise email servers provide secure mail, using techniques such as PGP and S/MIME. In short, the work required to build security into the application layer is reduced.

  • Leverages existing infrastructure. Email is the first enterprise solution any company implements. Most organizations have sophisticated hardware and software infrastructures, to assure a high quality of service for this corporate lifeline. Messaging with JavaMail and XML can leverage this existing infrastructure to build interoperable asynchronous messaging solutions.

  • Provides message sorting facilities. Messages in the server can be searched, sorted, and filtered from applications using headers (subject, to, from, etc.) and even message content, if needed.

  • Supports notifications. Mailbox monitoring features allow monitoring of message servers for new messages rather than using a latent polling approach.

  • Supports delivery to multiple recipients. The same message can be delivered to multiple recipients. Audit trail services can also be built, using the "bcc" feature.

  • Reduces implementation cost. Messaging solutions using JavaMail are generally less expensive, because most of the infrastructure already exists, and no MOM products are required.

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.

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 Figure 11.5 can be a good fit. If is this is not the case, see Chapter 14 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 applications multiple times. In short, a mail based solution offers no guaranteed one-time delivery support . The application 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 Web Services Architecture
Java Web Services Architecture (The Morgan Kaufmann Series in Data Management Systems)
ISBN: 1558609008
EAN: 2147483647
Year: 2005
Pages: 210

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net