Manage users with JMS

Use JMS to build a unified user management framework within your enterprise

The importance of maintaining an accurate list of an enterprise's customer and user profiles cannot be overemphasized. Most enterprises today use business applications like enterprise resource planning (ERP), customer relationship management (CRM), supply chain management (SCM), and some homegrown applications for e-commerce. A common communication infrastructure is required to maintain a synchronized list of user profile information among these diverse applications. Any system providing such an infrastructure must ensure that communication among these applications is reliable (even over unreliable networks) so that user information is not lost; user information is accurate and consistent within these applications; and the solution can scale well as new users and applications are added.

Given these stringent requirements, protocols like HTTP, FTP, remote procedure call (RPC), and Remote Method Invocation (RMI) are ruled out. These are synchronous protocols, which, by definition, require an active physical connection between the client and server to function reliably. Certainly, a more robust solution is required. Messaging provides the ability for the client and server to operate asynchronously in a disconnected manner, just like email. At the same time, messaging systems like Java Message Service (JMS) offer native support for reliable, guaranteed message delivery. Clearly, if we can send user profile updates to interested applications via messages, we have a solution for our problem.

Regarding the messages' data format, we use XML for message delivery because it is easily understood by diverse applications; it is a self-describing, extensible format that should accommodate any future applications without modifying existing ones; XML content is easily searchable; and XML parsers are readily available and easy to use.

This article assumes an introductory knowledge of JMS or other message-queuing systems. Rather than describe JMS capabilities, we focus more on our framework's architecture and design. We provide a system architecture overview, a class diagram, a sequence diagram, and Java code to clearly describe the solution we developed. The solution was deployed on BEA's WebLogic Server 5.1.0 SP8 (Service Pack 8), which implements Sun Microsystems' JMS 1.0.1.

Note: You can download this article's source code from Resources.

System architecture

Figure 1 shows our system's architecture. The system consists of a Web interface for user administration. An administrator uses this interface to change a user's profile. This Web interface uses a custom API, which we call UserInfo API for convenience, to handle updates to user profiles. Each time a user profile is modified, UserInfo first updates the master database. The master database is updated first so that in the (rare) case of a transmission failure, the data can be published again using information just saved in the master database. Once the master database has been updated, the JMS publisher is notified. The publisher then publishes the changes to the JMS topic. The various registered application subscribers then asynchronously receive these changes.

Figure 1. System architecture. Click on thumbnail to view full-size image.

One JMS subscriber is assigned to each business application that needs to locally maintain user profile information. In the accompanying architecture diagram, JMS subscribers are provided for an ERP application, a CRM application, and a future application (that we call XYZ application). Each subscriber updates the user profile information directly in its corresponding application, either via an API to the application (in which case the application must be Java-based), or by manipulating the tables in the application (in which case the application need not be Java-based).

Users can modify their own profiles through a personalized My Profile page. All changes made by users to their profiles are also sent to the publisher so it can notify the subscribers.

Prepare the message

The javax.jms.TextMessage object updates the user profiles. Each TextMessage encapsulates an XML document as a Java String that contains updates to a user's profile. The XML document specifies the operation to be performed, the username on which the operation will be performed, and the updated user information.

For example, the following XML message adds a new user to the system:

   <UserProfiles>
      <User>
         <Action>Create</Action>
         <Username>tbrown</Username>
         <FirstName>Timothy</FirstName>
         <LastName>Brown</LastName>
         <Email>tbrown@somewhere.com</Email>
      </User>
   </UserProfiles>

The following XML message deletes a user's profile from the system:

   <UserProfiles>
      <User>
         <Action>Delete</Action>
         <Username>tbrown</Username>
      </User>
   </UserProfiles>

The publication mechanism

We use the publish-subscribe messaging model defined by the JMS specification to deliver user profile updates to the various subscriber applications. In this model, publishers send messages to a topic, and all subscribers to that topic receive all messages sent to that topic. The publishers and the subscribers need not be aware of each other—the JMS implementation distributes the messages arriving from a topic's publisher to its multiple subscribers. The default message delivery mode is persistent, which ensures that messages are not lost in case of a JMS provider failure; and the subscriptions can be made durable, which ensures that if a subscriber is inactive, the persisted message is not lost, but rather, is delivered when the subscriber becomes active again. That guarantees message delivery.

We use the publish-subscribe model because each message must be consumed by multiple recipients, and these recipients can be added and removed at will. We use nondurable subscribers instead of durable subscribers because according to the JMS specification, only one durable subscriber can be active at one time; we need to maintain multiple subscribers. Nondurable subscribers do not pose a problem for our application because we expect active subscribers at all times. However, it is important we use persistent message delivery mode to ensure that messages always reach the subscribers. In the persistent delivery mode, a message is not considered published until it has been successfully written to a (properly configured) database or file system.

Class structure

Figure 2 shows our framework's class diagram. The diagram's various components are explained below.

Figure 2. Class diagram shows a generic interface and a JMS implementation. Click on thumbnail to view full-size image.
  • UserInfo API: The Web application uses this API to communicate with the messaging system. It is a generic API—it does not assume anything about the underlying implementation. Any other implementation can easily be supported as long as it conforms to the interface provided by this API.
  • UserServiceFactory: This class implements the popular Factory pattern. It returns an implementation of IUserService (in this case JMSUserService).
  • IUserService: This interface to JMSUserService decouples the application from the specifics of a particular implementation.
  • JMSUserService: This class handles the user profile data. It prepares XML messages for publication to the topic and uses JMSClientManager to publish them.
  • ServicesStartup: This class is called by the application server when the server starts up. ServicesStartup calls JMSClientManager, which creates the publisher and subscribers, so the publisher and subscribers are set up before the server begins to listen on its designated port.
  • JMSClientManager: This singleton class creates one Publisher object and all the Subscriber objects. It registers the subscribers as listeners to the JMS topic.
  • Publisher: Created by JMSClientManager, this class publishes data to the JMS topic.
  • Subscriber: This is the parent class of all application subscribers. JMSClientManager expects an instance of this class while registering a subscriber. Subscriber implements javax.jms.MessageListener.

The subscribers

The subscriber classes (ERPSubscriber, CRMSubscriber, etc.) are subscribers for any enterprise applications needing updates to user profiles. They receive user profile changes from the JMS topic in the form of XML messages and then update these profiles in the applications to which they are bound.

These classes use instanceof to ensure they have received a message of type TextMessage. They ignore all other message types.

These classes inherit from Subscriber, which means they must override the onMessage() method. The onMessage() callback is an event-driven message handler that the JMS provider asynchronously invokes when a new message arrives. This method extracts the message's content and reconstructs it into a meaningful transaction for updating a user's profile. We provide a template for the onMessage() method implemented by ERPSubscriber below:

   public void onMessage(Message message)
   {
      try {
          // Message must be of type TextMessage.
          // Ignore other message types.
          if (message instanceof TextMessage) {
              TextMessage textMessage = (TextMessage) message;
              String profileXML = textMessage.getText();
              // One of the elements in the document must be 'Username',
              // so we know which user's profile needs to be updated.
              // Also, one of the elements in the document must be 'Action',
              // so we know what action is to be performed on the user profile
              // (Create, Update, or Delete).
              // Retrieve the following from the XML document:
              // 1) Username whose profile needs to be updated
              // 2) Action to take with the user profile
              // 3) Parameters that need to be updated,
              //    e.g., address, phone number, etc.
              ...
              ...
              // Update ERP system with new user data.
              ...
              ...
              }
          }
          else {
              System.out.println("Message not recognized.");
              return;
          }
      }
      catch (JMSException e) {
          e.printStackTrace();
      }
   }

Set up the publisher and subscribers

Some session objects must be created as part of the publisher-subscribers setup. Exercise caution while developing these session objects. If you look at JMSClientManager, you notice we create one session for the publisher and one session for each subscriber. (It's a different matter that we create all our subscriber sessions with the same TopicSession object (subSession).) For example, the code below creates a new TopicSession every time a new subscriber is added to the JMS topic:

    // This method is extracted from JMSClientManager.java
    public void registerSubscriber(Subscriber subscriber)
    {
        try {
            // Create a new TopicSession object for every subscriber
            subSession = topicConnection.createTopicSession(
                                 false, Session.AUTO_ACKNOWLEDGE);
            topicSubscriber = subSession.createSubscriber(topic);
            topicSubscriber.setMessageListener(subscriber);
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }

Since we have one publisher and three subscribers, we create four different sessions rather than have all objects use the same session. This is because, according to the JMS specification, TopicSession does not support multiple threads. In our application, each subscriber uses a different thread, and the publisher uses a different thread (which is the main application thread). Since the invocation of the onMessage() handler is asynchronous, it can be called while the publisher is publishing messages in another thread. If the same session creates the publisher and subscribers, the two would operate in the same session concurrently—a condition not supported by the JMS specification. For the same reason, each subscriber must also operate in a different session. Since the invocation of the onMessage() handler is asynchronous, two subscriber threads can become active at the same time, so they should not use the same session.

Set up the publisher and subscribers following these steps:

1 2 Page 1