Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

Wireless messaging with JXTA, Part 2: Implement JXTA-for-JMS

Integrate J2ME clients in enterprise messaging solutions

Faheem Khan (fkhan872@yahoo.com), Freelance Consultant
Faheem Khan is an independent software consultant specializing in Enterprise Application Integration (EAI) and B2B solutions. He can be reached at fkhan872@yahoo.com.

Summary:  Learn how to implement the bridge between a Java™ 2 Platform, Micro Edition (J2ME) client and a Java Message Service (JMS) application using JXTA (JXTA-for-JMS, or JXTA4JMS for short). Use cases show how a J2ME device or a JMS client uses JXTA4JMS. It then describes the JXTA4JMS architecture and explains the arrangement of classes in the JXTA4JMS implementation.

Date:  22 Feb 2005
Level:  Intermediate

Comments:  

Introduction

In the first article of this series, you learned why Java 2 Platform, Micro Edition (J2ME)-enabled devices can't directly host client applications for enterprise messaging. That article also discussed how to use JXTA technology to integrate thin mobile clients in enterprise messaging solutions and demonstrated how to use JXTA protocols in a J2ME device.

This second article of the series shows you how to implement the bridge between a J2ME client and a JMS application using JXTA (JXTA-for-JMS, or JXTA4JMS for short). The JXTA4JMS implementation consists of two components: One runs inside a desktop PC (desktop-side JXTA4JMS); the other component runs inside a J2ME-based mobile device (mobile-side JXTA4JMS).

This article begins with a few use cases to show how a J2ME device or a JMS client uses JXTA4JMS. It then describes the JXTA4JMS architecture and explains the arrangement of classes in the JXTA4JMS implementation. Finally, the article demonstrates the actual implementation and provides a working application that integrates thin J2ME clients into Java Message Service (JMS) applications.

Consider a company that uses JMS for its enterprise-wide messaging requirements. The company has hosted a JMS provider, and its employees who use the JMS network act as messaging clients (message producers or consumers). The company wants the messaging clients to remain connected to each other. The JMS network fulfills this requirement as long as all users have access to their PCs.

JXTA4JMS is needed when users do not have access to their PCs (for example, when they are out of office). Suppose JMS user Alice leaves the office. She enables JXTA4JMS on her desktop and on her cell phone. The desktop-side JXTA4JMS starts listening for incoming JMS messages. Whenever it receives a JMS message for Alice, it forwards the message to Alice's cell phone. Similarly, when Alice wants to send a message to a colleague, she uses the mobile-side JXTA4JMS to send that JMS message. The recipient of the message receives the message as a normal JMS message. JXTA4JMS helps Alice to remain connected and continue messaging with the JMS network using her J2ME-enabled cell phone.

Notice the important feature of JXTA4JMS: It does not disturb the work of the other JMS users. If Alice no longer wants to use JXTA4JMS, she simply disables it. JXTA4JMS is transparent for other JMS users; they aren't affected and, therefore, do not know whether Alice enables or disables JXTA4JMS.

As you can see, JXTA4JMS handles two-way messaging between JMS clients and mobile clients. Let's consider these two use cases separately to clearly define the functionality of JXTA4JMS.


Messaging from a JMS client to a J2ME client

Suppose Bob wants to send a JMS message to Alice. Bob uses his desktop to send the message. Although Bob doesn't know this, Alice is not in her office at the time, but while leaving her office, she enables JXTA4JMS on her desktop and on her J2ME cell phone.

As soon as Alice enables JXTA4JMS on her desktop and cell phone, both sides prepare for a message exchange:

  1. The desktop-side JXTA4JMS starts listening for JMS messages on Alice's queue on the JMS provider.

  2. The mobile-side JXTA4JMS creates an input JXTA pipe and begins listening for incoming messages. The desktop-side JXTA4JMS searches for this pipe, treats it as an output pipe, and uses it to send messages to the mobile phone. Therefore, this pipe is used to send messages from the desktop to the mobile phone. Note that a JXTA pipe is a virtual communication channel with two ends (incoming and outgoing). The peer who creates the pipe sits at the incoming end, and the one who searches for it sits at the outgoing end.

  3. You need another JXTA pipe to send messages from the mobile to the desktop. The desktop-side JXTA4JMS creates this pipe and starts listening for incoming messages on the pipe from the mobile-side JXTA4JMS. The mobile-side JXTA4JMS searches for the pipe and uses it to send JMS messages to the desktop.

Now, two JXTA pipes exist: one for messages from desktop side to mobile side, and the other for mobile side to desktop side.

Now look what happens when Bob (a JMS user) sends a message to Alice (a JXTA4JMS user). Figure 1 shows a graphical view of the sequence of events.


Figure 1. Sending a message from a JMS client to a J2ME client
Sending a message from a JMS client to a J2ME client

  1. Bob sends a JMS message to Alice's queue on the JMS provider.

  2. The JXTA4JMS implementation on Alice's PC constantly listens for messages on Alice's queue, so JXTA4JMS receives the JMS message from Bob.

  3. JXTA4JMS now translates the JMS message into a JXTA format that can travel across the JXTA network. Alice's mobile client uses JXME to communicate with the JXTA network. Recall from the discussion in this series' first article that JXME clients don't talk directly to JXTA peers; they use relays to communicate with the JXTA network. However, this process is transparent to JXTA peers; they never know whether a particular message is routed through a relay or sent directly to the ultimate recipient. That's why the JXTA4JMS implementation authors a JXTA message (and not a JXME message, the structure of which was explained in the first article).

  4. JXTA4JMS sends the message to the JXTA relay over the JXTA pipe the mobile-side JXTA4JMS creates. It is the JXTA network's responsibility to properly route the message over the output pipe and through the relay.

  5. The JXTA relay automatically translates the message from a JXTA format to a JXME format before delivering the message to the mobile client.

  6. The mobile-side JXTA4JMS implementation inside Alice's cell phone constantly listens for messages on the same pipe. So it receives Bob's message.

  7. The JXTA4JMS displays the message on Alice's cell phone screen.

Messaging from a J2ME client to a JMS client

Now, look at how JXTA4JMS helps Alice send a reply message to Bob. Figure 2 illustrates a graphical view of the scenario.


Figure 2. Sending a message from a J2ME client to a JMS client
Sending a message from a J2ME client to a JMS client

The scenario in Figure 2 is similar to the one in Figure 1:

  1. Alice writes her response message to Bob and asks the mobile-side JXTA4JMS implementation running on her cell phone to send it to Bob.

  2. JXTA4JMS wraps Alice's message in the JXME message format discussed in the first article. This JXME message also wraps the name of the JMS message recipient (in this case, Bob).

  3. JXTA4JMS sends the message to a JXTA relay. Note that this message is sent over the JXTA pipe that Alice's desktop-side JXTA4JMS creates.

  4. The relay translates the JXME format into JXTA format.

  5. The relay sends the JXTA message to Alice's desktop over the JXTA network, where her desktop-side JXTA4JMS is enabled.

  6. The desktop-side JXTA4JMS receives the message from the relay. Note that the message is now in JXTA format.

  7. The desktop-side JXTA4JMS translates the message to JMS format. It extracts the JMS client username Alice intends to message (Bob, in this case). It also searches for Bob's queue on the JMS provider.

  8. The desktop-side JXTA4JMS sends the message to Bob's queue on the JMS provider.

  9. Bob's client-side JMS application listens on his queue. It receives the message from Alice.


The JXTA-for-JMS architecture

Let's examine the architecture of the JXTA4JMS implementation. As you've already seen in the discussion of the JXTA4JMS usage model, the JXTA4JMS implementation consists of the desktop and mobile-side implementations. Take a look at both implementations.

The desktop-side implementation consists of several layers, as Figure 3 shows. A layered architecture ensures better reusability of JXTA4JMS. You can easily modify any of the JXTA4JMS layers according to your own requirements and still use the rest of the layers as such.


Figure 3. The desktop-side JXTA4JMS layers
The desktop-side JXTA4JMS layers

Following are explanations of each JXTA4JMS layer:

  • The listener layer listens to JMS messages coming from the JMS network as well as JXTA messages coming from the J2ME device. The listener layer also establishes a JMS connection with the provider and creates pipes on the JXTA network to communicate with the J2ME mobile device. This layer consists of just one class named Listener.

  • The router layer has methods that can route JMS messages to the J2ME device (through the JXTA network), as well as J2ME messages to the JMS clients. This layer consists of one class named Router. The Listener class listens to incoming messages and uses the Router class for appropriate routing of messages.

  • The format conversion layer converts JMS messages to a JXTA format and vice versa. This layer consists of two classes: JXTAToJMSMsgConverter and JMSToJXTAMsgConverter. The Router class uses the format conversion classes to translate message formats before routing them to their destination.

  • The networking layer handles the low-level details of working with both JMS and JXTA networks. This layer consists of classes JXTASearch and JMSSearch. The JXTASearch class searches for a JXTA pipe the J2ME device creates and publishes over the JXTA network. This pipe is used to send messages to the J2ME device. The JMSSearch class searches for the JMS queue of the JMS user (recipient of the J2ME client's message). The Listener and Router classes use these two JXTASearch and JMSSearch classes.

The mobile-side implementation of JXTA4JMS handles the wireless messaging (the sending and receiving of messages to and from the desktop-side JXTA4JMS implementation). A set of four classes, described next, works in a layered architecture to perform the messaging task.

  • The JXMEMIDlet class is a sample MIDlet application I wrote to demonstrate the use of the JXTA4JMS messaging client with a simple user interface.

  • The JXTA4JMSMessagingClient class is the most important class in the J2ME-side implementation. The JXTA4JMSMessagingClient class provides all low-level functionality such as connecting to a relay and exchanging and processing messages. This allows the higher-level application to focus only on user interface items.

  • The JXTA4JMSMessage class authors and processes JXTA4JMS messages. A JXTA4JMS message is a JXME message customized for this application. The JXTA4JMSMessagingClient uses this class to author and process JXTA4JMS messages.

  • The DataStore class is a low-level utility class that stores the identifier of the pipe used to receive messages from the JMS-side implementation. The DataStore class handles all low-level details of J2ME record store use. The JXTA4JMSMessagingClient class uses this utility class to store and retrieve the pipe identifier.


Configure JXTA4JMS

Before you implement JXTA4JMS, you should first look at how to configure JXTA4JMS in a JXTA network. This helps you visualize how JXTA4JMS uses JXTA rendezvous peers and relays.

JXTA4JMS needs three JXTA instances running. The first instance represents the JMS user on the JXTA network (for example, Alice). This JXTA instance is part of the JXTA4JMS instance.

The second instance is a rendezvous peer. A rendezvous peer is a meeting place for all JXTA peers. It keeps JXTA advertisements cached with it, thus allowing JXTA peers to discover the presence of other peers in the network. I describe rendezvous peers later.

The third instance is a JXTA relay the mobile JXTA4JMS uses to communicate over the JXTA network.

The three JXTA instances' configuration requirements are different, which I will explain momentarily. In real-world applications, the three instances run on different machines connected to each other through some networking technology (for example, the Internet).

To see JXTA4JMS run on a single computer that might not be connected to any network, you can have the three instances running on the same machine. This JXTA4JMS implementation does not care whether the different instances run on the same machine or different machines. You can also configure the same JXTA instance to play two or all three roles.

I packaged this article's source code in the wi-jxta2source.zip file, where you will also find a readme file explaining how I tested JXTA4JMS.

When you run a JXTA instance for the first time, you see a JXTA configuration window. You need to fill in the different fields in the configuration window only once for each instance. The configuration window consists of basic, advanced, rendezvous/relays, and security tabs. The entries for basic and security tabs are the same for all three JXTA instances. Figure 4 displays the basic tab.


Figure 4. The basic tab in the JXTA configuration window
The basic tab in the JXTA configuration window

The basic tab contains a text box for the peer name. For example, Alice fills this box with the string "Alice" when she configures the JXTA instance that represents her on the JXTA network. When you configure the rendezvous and relay peers, you can give any names.

The basic tab also contains a checkbox named "Use proxy server." Check this box only if you are behind a firewall.

The security tab contains fields to enter logon information for the JXTA instance. Figure 5 shows the security tab containing secure username and password fields.


Figure 5. The security tab in the JXTA configuration window
The security tab in the JXTA configuration window

The secure username is different from the peer name field in the basic tab. The peer name field specifies the name of the peer in the JXTA network, while the secure username field represents the login name of the particular JXTA instance. You need the secure username and password every time you start the JXTA instance.

For the sake of simplicity, you can use the same name (for example, Alice) as entries for the peer name and secure username fields.

Now let's see how to configure the advanced tab. The advanced tab has two sections, one for TCP and one for HTTP settings, as shown in Figure 6. The TCP and HTTP settings include the IP address and port number where this particular JXTA4JMS instance is listening. By default, JXTA configuration uses port 9700 for HTTP and 9701 for TCP.


Figure 6. The advanced tab in the JXTA configuration window
The advanced tab in the JXTA configuration window

If you use different machines for the three JXTA instances, then you just need to specify the IP address of the respective machine. If you run more than one JXTA instance on the same machine, then you need to use different TCP and HTTP port numbers for each JXTA instance.

For example, when I tried this application on a single machine, I configured the JXTA4JMS instance for Alice with port number 9700 and 9701 for HTTP and TCP, respectively. I configured a second JXTA instance to act as both rendezvous and relay peers. This second instance listened at port numbers 9702 and 9703 for HTTP and TCP, respectively.

When you configure the rendezvous and the relay peers, you must do one more thing on the advanced tab: click the Enable Incoming Connections checkbox in the HTTP settings section. This allows other peers to have an HTTP connection with rendezvous and relay peers.

The rendezvous/relays tab shown in Figure 7 contains two sections, one for rendezvous peers and one for relay peers.


Figure 7. The rendezvous/relays tab in the JXTA configuration window
The rendezvous/relays tab in the JXTA configuration window

When you configure the relay peer, you must check the Act as a JxtaProxy and Act as a Relay checkboxes in the rendezvous/relays tab. When you configure the rendezvous peer you must check the Act as a Rendezvous checkbox.

You also need to give the IP address and port number of the rendezvous instance to the other two JXTA instances (the JXTA4JMS instance and the relay instance). For this purpose, you must fill the Available TCP rendezvous and Available HTTP rendezvous fields with the IP address and port number of the rendezvous peer.

Now a brief note on rendezvous peers. A real-world JXTA network contains many rendezvous peers. If Bob and Alice communicate over the JXTA network, each of them will know at least one rendezvous peer. It is not necessary that they both know the same rendezvous peer.

If Bob knows a rendezvous peer R1 and Alice knows rendezvous peer R2, and R1 and R2 rendezvous peers know a common rendezvous peer R3, then Bob and Alice are able to communicate seamlessly over the JXTA network. The JXTA network automatically manages the discovery of peers without Bob and Alice worrying about these details. This is an important feature of the JXTA technology.

Now let's discuss JXTA4JMS implementation details for the desktop side and then the mobile side.


Implement the desktop-side JXTA4JMS

I already discussed the function of various layers in the JXTA4JMS architecture, so I will now demonstrate the implementation of these layers. I move from bottom to top in this implementation discussion because upper layers use the functionality of the lower layers.

The JXTASearch class

The JXTASearch class searches for a particular pipe on the JXTA network. The JXTASearch class searches the pipe in a synchronous way; this means that a call to the JXTASearch class does not return until it finds the pipe. The pipe you want to search is the pipe that the mobile-side has created, published, and is listening at. You use this pipe to send messages to the J2ME device.

The JXTASearch class contains a constructor and two methods: getPipeAdvertisement() and getOutputPipe(). In addition, the JXTASearch class also implements an interface named OutputPipeListener. According to the JXTA implementation, any class that wants to receive notifications about a pipe creation implements the OutputPipeListener interface. The OutputPipeListener interface has a single method named outputPipeEvent() that receives the pipe resolving notifications from the underlying JXTA implementation. You will soon see how the JXTASearch class implements the outputPipeEvent() method.

The JXTASearch constructor

The JXTASearch constructor (Listing 1) takes an instance of the PeerGroup class. The PeerGroup class is a convenient way to interact with a JXTA peer group. It contains methods to perform various tasks you might want to perform in a peer group. I'll describe how to use two methods of this class later. For now, just note that the JXTASearch constructor simply stores the PeerGroup object in a class-level variable (named peerGroup) for later use by the getPipeAdvertisement() and getOutputPipe() methods.


Listing 1. The JXTASearch constructor
    public JXTASearch ( PeerGroup peerGroup ){
        this.peerGroup = peerGroup;
    }

The getPipeAdvertisement() method

The getPipeAdvertisement() method shown in Listing 2 takes a pipe name and searches for the advertisement corresponding to the pipe. The Listener class specifies the name of the pipe while it calls the getPipeAdvertisement() method.

The getPipeAdvertisement() method returns the pipe advertisement in the form of a PipeAdvertisement object.


Listing 2. The getPipeAdvertisement() method
    public PipeAdvertisement getPipeAdvertisement ( String targetPipeName ) {
        Enumeration enum = null;
        PipeAdvertisement pipeAdvert = null;
       
        DiscoveryService discoveryService = peerGroup.getDiscoveryService ();

        try
        {
            while( true ){
                discoveryService.getRemoteAdvertisements (
                                                     null, 
                                                     DiscoveryService.ADV, 
                                                     "Name", 
                                                     targetPipeName, 
                                                     5
                                                     );

                enum = discoveryService.getLocalAdvertisements (
                                                           DiscoveryService.ADV, 
                                                           "Name", 
                                                           targetPipeName
                                                           );

                
                if ( enum != null)
                {
                    while (enum.hasMoreElements()) 
                        pipeAdvert = ( PipeAdvertisement ) enum.nextElement ();
                }//if(enum != null)
                
                if ( pipeAdvert != null ) 
                    break;
        
            }//while( true )
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
        
        return pipeAdvert;
        
    }//getPipeAdvertisement() 

The first order of business in the getPipeAdvertisement() method is to call the getDiscoveryService() method of the PeerGroup object stored in the constructor. This getDiscoveryService() method call returns a DiscoveryService object.

The DiscoveryService class represents a JXTA service known as discovery service. JXTA peers use the discovery service to search for different resources on the JXTA network. I use the JXTA discovery service to search for a particular pipe in the specified peer group.

If you want to use the JXTA discovery service, you need to author the XML search query and send the query to the rendezvous peer discussed earlier in Configure JXTA4JMS. Moreover, you must process incoming messages in response to your search query.

However, the JXTA implementation saves you from all this. The DiscoveryService class wraps the functionality of the discovery service nicely. You just call a few methods of the DiscoveryService class without worrying about what XML syntax JXTA requires and uses to respond. The JXTA implementation takes care of these low-level details.

After you have the DiscoveryService object, you can use its methods to perform the pipe search operation. The DiscoveryService class has a method named getRemoteAdvertisements() that sends a search query out on the JXTA network. The getRemoteAdvertisements() method takes five parameters:

  • The first parameter is an identifier. If you know the pipe identifier you are searching, you can provide the identifier as the first parameter to the getRemoteAdvertisements() method to speed up the remote search operation. However, in this case, you don't know the identifier of the pipe you are searching. So you simply pass null as the value of the first parameter to the getRemoteAdvertisements() method call.

  • The second parameter specifies the type of resource you are searching (for example, an advertisement, a peer, or a peer group). You are searching for a pipe advertisement, so you simply use a static field named ADV of the DiscoveryService class. If you were searching for a peer or a peer group, you would specify PEER or PEERGROUP static fields, respectively.

  • The third parameter is the name of the attribute you want to search for. You are searching for a pipe advertisement with a particular name. So, you search for a pipe with a particular value of the "Name" attribute. This is analogous to the second parameter to the search() method discussed in the "JXME programming" section of the first article. Therefore, you pass the string "Name" as the third parameter to the getRemoteAdvertisements() method call.

  • The fourth parameter is the name of the pipe you are searching (or you can say the value of the "Name" attribute). You simply pass the name of the pipe as this value.

  • The last parameter to the getRemoteAdvertisements() method is the maximum number of search results you want to receive. You are not sure how many peers will respond to your search query and how many responses each peer will send. Therefore, you want to limit the search operation. You pass "5" as the value for the last parameter, which means you want to receive a maximum of five results from each peer.

The getRemoteAdvertisements() method does not return anything. It stores the search results in a local cache that the JXTA implementation maintains. Therefore, after calling the getRemoteAdvertisements() method, you must call another method named getLocalAdvertisements() to retrieve the search results from the local cache.

The getLocalAdvertisements() method takes three parameters: resource type, name of the attribute, and its value. These three parameters are the same as the second, third, and forth parameters to the getRemoteAdvertisements() method.

The getLocalAdvertisements() method returns an Enumeration object. The Enumeration object contains the search results in the form of a number of Advertisement objects.

You can expect different types of Advertisement objects in the Enumeration object the getLocalAdvertisement() method returns. As you search for pipe advertisements, you can expect that the Enumeration object, if not null, will include one or more PipeAdvertisement objects. The PipeAdvertisement object represents a pipe advertisement.

Let's go through the whole Enumeration and pick the last advertisement because the last is the most recent one.

The getOutputPipe() method

The getOutputPipe() method, shown in Listing 3, takes a PipeAdvertisement object as a parameter. Normally, this parameter is the same PipeAdvertisement object that you search for in the getPipeAdvertisement() method.

The getOutputPipe() method returns an OutputPipe object. This OutputPipe object represents the JXTA pipe corresponding to the pipe advertisement.


Listing 3. The getOutputPipe() method
    public OutputPipe getOutputPipe (PipeAdvertisement pipeAdvert){
        try
        {
            PipeService pipeSvc = peerGroup.getPipeService ();
            pipeSvc.createOutputPipe ( pipeAdvert, this );

            while ( true )
            {
                if ( outputPipe != null )
                    break;
            }//while (true)
         
         return outputPipe;        
        
        }//try
        catch ( Exception e ){
             e.printStackTrace();
        }//catch
         return null;
    }//getOutputPipe()

Just as you need the JXTA discovery service to search for pipe advertisements, you need the JXTA pipe service to create an output pipe. The JXTA implementation provides a class named PipeService that wraps the functionality of the JXTA pipe service. You use the PeerGroup class's getPipeService() method to get the PipeService object.

The PipeService class has createInputPipe() and createOutputPipe() methods to create input and output pipes for message exchange. The PipeService class's createOutputPipe() method takes two parameters: PipeAdvertisement and OutputPipeListener objects. It creates a JXTA pipe using the advertisement and returns an OutputPipe object.

The JXTA pipe creation process occurs over the JXTA network. I won't get into those details, but the createOutputPipe() method can't wait for the pipe to be created, so it returns asynchronously.

The JXTA network confirms the pipe creation later in the form of a pipe-resolving event. According to the JXTA implementation, only the class that implements the OutputPipeListener interface can receive pipe-resolving event notification. The OutputPipeListener interface contains just one method named outputPipeEvent(). The JXTASearch class implements the outputPipeEvent() method so it can receive the notification.

When the notification arrives, the outputPipeEvent() method takes control. The outputPipeEvent() implementation is simple; Listing 4 shows that it only takes one line.The outputPipeEvent() method stores the newly created pipe in a class-level object named outputPipe.


Listing 4. The outputPipeEvent() method
    public void outputPipeEvent ( OutputPipeEvent e ) {
        outputPipe = e.getOutputPipe ();
    }//outputPipeEvent()

To simplify the use of the JXTASearch class, I blocked the getOutputPipe() method in an infinite while loop that breaks only when the notification arrives. The infinite while loop keeps checking whether the outputPipeEvent() method has set the outputPipe class-level object. When the getOutputPipe() method finds the required OutputPipe object, it returns the same.

The JXTAToJMSMsgConverter class

The JXTAToJMSMsgConverter class takes a JXTA message and converts it into its equivalent JMS message. The JXTAToJMSMsgConverter class contains four methods (a constructor and three getter methods) as explained below:

The JXTAToJMSMsgConverter constructor

Listing 5 shows the JXTAToJMSMsgConverter constructor.


Listing 5. The JXTAToJMSMsgConverter constructor
    public JXTAToJMSMsgConverter (
                              QueueConnectionFactory qConnFactory, 
                              net.jxta.endpoint.Message jxtaMsg
                            ) throws JMSException {
          QueueConnection queueConnection = null;
  
          try
          {
              //Creating a new JMS text message.
              //Step 1:
              queueConnection = qConnFactory.createQueueConnection ();
              //Step 2:              
              queueSession = 
                     QueueConnection.createQueueSession (
                                               false, 
                                               Session.AUTO_ACKNOWLEDGE );
              //Step 3:
              jmsMessage = queueSession.createTextMessage ();
          }
          catch ( Exception e ) {
              e.printStackTrace ();
          }

        MessageElement jmsRecipientElement, msgElement;
        jmsRecipientElement = jxtaMsg.getMessageElement ("JMSRecipient");
        msgElement = jxtaMsg.getMessageElement ("Message");

        jmsRecipient = jmsRecipientElement.toString();
        jmsMessage.setText (msgElement.toString());
        
    }//JXTAToJMSMsgConverter


The constructor takes two parameters, a QueueConnectionFactory object named qConnFactory and a net.jxta.endpoint.Message object named jxtaMsg. I'll examine these objects more closely.

The Listener class instantiates a QueueConnectionFactory object and passes the object as the first parameter to the JXTAToJMSMsgConverter constructor. The QueueConnectionFactory is a connection factory and a JMS-administrated object. According to the JMS architecture, you need a QueueSession object to create a new JMS message. And you need a queue connection factory (a QueueConnectionFactory object) to create a QueueSession object. You will see how to create a JMS message from the connection factory later.

The net.jxta.endpoint.Message object (the second parameter to the JXTAToJMSMsgConverter constructor) is the JXTA message you want to convert to JMS format. Look at how the JXTAToJMSMsgConverter constructor works.

You must first create a new JMS message object, which is done in three steps. First, you call the QueueConnectionFactory object's createQueueConnection() method. This gives you a QueueConnection object. You then call the QueueConnection object's createQueueSession() method, which gives you a QueueSession object. In the third step, you call the QueueSession object's createTextMessage() method, which returns a javax.jms.TextMessage object. This javax.jms.TextMessage object is named jmsMessage. This is the JMS message object you now populate with data from the JXTA message. Notice these three steps in the "Creating a new JMS text message" in Listing 5.

At this point, note that a QueueSession object has methods to create various types of JMS messages. For example, you might want to create a message in binary form using the QueueSession class's createByteMessage() method. However, I chose to use the createTextMessage() method because I want to demonstrate the exchange of text messages in this article series.

Now take a look at how to extract data from the incoming JXTA message and populate the JMS message with that data.

The incoming JXTA message contains two parts: the name of the intended message recipient and the message itself. Each part of the JXTA message comes as an element in the JXTA message. The intended JMS recipient's name is wrapped inside an element called "JMSRecipient." The message is wrapped in an element named "Message.

You can call the net.jxta.endpoint.Message class's getMessageElement() method, passing the name of the element along with the method call. The getMessageElement() method returns a MessageElement object, which represents a single element of the JXTA message.

I explain the structure of an individual element in a JXTA message later. For now, just note from Listing 5 that the two MessageElement objects are named msgElement (for the object that wraps the message) and jmsRecipientElement (for the object that wraps the name of the recipient).

Now you can call the toString() method of each MessageElement object. This method returns the MessageElement's contents in string form.

You can then call the setText() method of the jmsMessage object (the javax.jms.TextMessage object created earlier) and pass the JXTA message contents along with the method call. This sets the JXTA message contents in the jmsMessage object. The jmsMessage object is now ready.

The getQueueSession() method

The getQueueSession() method (shown in Listing 6) returns the QueueSession object created in the constructor. You see that the Router class calls the getQueueSession() method to fetch the QueueSession object. The Router class uses this QueueSession object to send the text message to the JMS recipient.


Listing 6. The get QueueSession() method
    public QueueSession getQueueSession() {
        return queueSession;
    }//getQueueSession()

The getMessage() method

The getMessage() method (shown in Listing 7) returns the javax.jms.TextMessage object named jmsMessage you created and populated in the constructor.


Lisitng 7. The getMessage() method
    public javax.jms.TextMessage getMessage() {
        return jmsMessage;
    }//getMessage()


The getJMSRecepient() method

The getJMSRecipient() method seen in Listing 8 returns the name of the recipient you extracted from the incoming JXTA message in the constructor earlier.

Also note in Listing 8 that you have concatenated a string "jms/" as a prefix to the name of the JMS recipient. Later, you use the name of the recipient as a JMS queue name and simply send the message on the queue. The "jms/" string follows the Java convention that names of the JMS queues start with the "jms/" string.


Listing 8. The getJMSRecipient() method
    public String getJMSRecipient() {
        return "jms/" + jmsRecipient;
    }//getJMSRecipient()

The JMSToJXTAMsgConverter class

JMSToJXTAMsgConverter is a message format converter class that takes a JMS message and produces a JXTA message. The JMSToJXTAMsgConverter class contains the methods described below.

The JMSToJXTAMsgConverter constructor

Now look at Listing 9, which shows the JMSToJXTAMsgConverter constructor.


Listing 9. The JMSToJXTAMsgConverter constructor
    public JMSToJXTAMsgConverter ( String sender, 
                                   TextMessage jmsMsg
                                 ) throws JMSException {
        StringMessageElement smeSender  = null;
        StringMessageElement smeMessage = null;

        jxtaMessage = new net.jxta.endpoint.Message ();
        smeSender  = new StringMessageElement ("JMSSender", sender, null);
        smeMessage = new StringMessageElement ("Message", jmsMsg.getText(), null);

        jxtaMessage.addMessageElement (smeSender);
        jxtaMessage.addMessageElement (smeMessage);

    }//JMSToJXTAMsgConverter constructor

The JMSToJXTAMsgConverter constructor performs almost all the processing required by this class. It takes two parameters. The first parameter is the name of the JMS client who sent the JMS message. You include the name of the sender in the JXTA message you author in the JMSToJXTAMsgConverter constructor. This lets the J2ME recipient know which JMS client sent this message. The second parameter is a JMS message in the form of a TextMessage object.

JXTA messages are authored corresponding to the incoming JMS message. The JXTA message consists of message elements. Therefore, I'll first look at the internal details of an individual message element in a JXTA message.

The structure of a JXTA message element is similar to the structure of a JXME message element (explained in the first article's "Messaging between a relay and a JXME client" section).

A JXTA message element contains the following four fields:

  • The name of the message element. In this case, you wrap the JMS message contents in an element named "Message." Similarly, you wrap the name of the JMS sender in an element named "JMSSender." Note that the name of the element is optional, so you can omit it. However, giving every element a name makes processing straightforward and easier.

  • The optional MIME type of the message element. If this field is not specified, its value is assumed to be "Application/Octet-Stream." In this case, the MIME type of the message you author is "text/plain."

  • The data you want to send. The message element, for example, contains the JMS message contents, and the sender element contains the name of the JMS sender.

  • The optional signature. This field contains the message element's cryptographic signature. At the moment, JXME does not support the inclusion of signatures on the J2ME side. Therefore, there's no reason to include signatures when you author your JXTA message.

You have seen the structure of a JXTA message element. The JXTA implementation has a class named MessageElement that handles all types of JXTA message elements. In addition, you have a number of subclasses derived from the MessageElement class. Each subclass handles a specific type of message element. For example, the StringMessageElement class handles the message elements that carry textual strings (the MIME type of such messages is "text/plain").

You use the StringMessageElement class to author the JXTA messages' individual message elements. You wrap the name of the JMS message sender in a StringMessageElement object named smeSender. Similarly, you wrap the JMS message contents in another StringMessageElement object named smeMessage.

To author a message with the StringMessageElement class, you must first instantiate the StringMessageElement class by calling its constructor. The constructor takes three parameters. The first parameter specifies the name of the message element. The second parameter specifies the contents or data of the element. The third parameter specifies the cryptographic signature. You aren't using one, so you pass on null as the value of the third parameter.

Look at the JMSToJXTAMsgConverter constructor in Listing 9. The two StringMessageElement objects named smeSender and smeMessage are instantiated. While creating the smeMessage object, you need the contents of the incoming JMS message. You use the TextMessage object's getText() method to extract the JMS message contents and then pass the contents to the StringMessageConstructor to author the smeMessage object.

Now you need to wrap the two StringMessageElement objects in a JXTA message. For this purpose, you must first instantiate a net.jxta.endpoint.Message object named jxtaMessage that represents a complete JXTA message. Next, you call its addMessageElement() method twice, once for each of the two StringMessageElement objects.

The getMessage() method

You use the getMessage() method shown in Listing 10 to retrieve the JXTA message. The method simply returns the jxtaMessage object you prepared in the constructor.


Listing 10. The getMessage() method
    public Message getMessage () {
        return jxtaMessage;
    }//getMessage()

The JMSSearch class

The JMSSearch class performs the searching in the JMS network just like the JXTASearch does in the JXTA network. However, searching in the JMS network is quite simple because you can use the Java Naming and Directory Interface (JNDI) for searching the different JMS resources. You can use JNDI to search for resources such as messaging objects (queues and connection factories), databases, and mail sessions.

Here you use JNDI to search for a queue. Look at the JMSSearch class in Listing 11.


Listing 11. The JMSSearch class
public class JMSSearch
{
    public JMSSearch (){
    }

    public QueueSender getQueueSender ( String queueName, 
                                       QueueSession queueSession ) {
        InitialContext jndiContxt = null;
        Queue outgoingQueue= null;
        QueueSender qSender = null;
 
        try
        {
            jndiContxt = new InitialContext ();
            outgoingQueue= (Queue) jndiContxt.lookup ( queueName );
            qSender = queueSession.createSender (outgoingQueue);
        }//try
        catch ( Exception e ) {
            e.printStackTrace ();
        }//catch
        
        return qSender;
        
    }//getQueueSender()
}//JMSSearch

The JMSSearch class contains just one method, getQueueSender(). This method takes two parameters, the name of the queue to search and a QueueSession object. This is the same QueueSession object introduced earlier in the discussion of the JXTAToJMSMsgConverter class's getQueueSession() method. I will explain the purpose of passing the QueueSession object as a parameter.

The getQueueSender() method returns the QueueSender object for this particular queue name.

The getQueueSender() method implementation, as you just saw in Listing 11, is simple. First, it instantiates an InitialContext object. This is a JNDI requirement. The InitialContext object has methods that perform the search operation for you.

Next, you simply call the lookup() method of the InitialContext class. In JNDI terminology, search is called lookup. The lookup() method takes the name of the queue as a parameter and returns a Queue object that represents the queue you are searching. The Queue object is named outgoingQueue because this Queue object represents the JMS queue of an intended message recipient. You use this queue later to send an outgoing message. For example, let's say Alice is on the road with her mobile-side JXTA4JMS and sends a message for Bob. Her desktop-side JXTA4JMS receives her message from the JXTA network, searches for Bob's queue on the JMS network, treats Bob's queue as the outgoingQueue object, and sends Alice's message to the outgoing (Bob's) queue.

Now you realize the importance of having the prefix "jms/" prepended before the queue name (recall that you prepended the "jms/" prefix in the JXTAToJMSMsgConverter class's getJMSRecepient() method). You don't just use JNDI lookup for searching JMS resources (other server-side modules use it for searching non-JMS resources like databases); so it's important to have some type of identifier to identify resources belonging to a particular group (for example, all resources belonging to the JMS network). If you don't have a string prepended to the queue name, you might confuse the queue name with some other non-JMS resource (such as a database table).

Although you have searched for the required Queue object, you want to do one extra step here. You want to create a QueueSender object from the Queue object you just searched. Later, the Router class simply uses the QueueSender object to send the message on the Queue.

To create a QueueSender object, you need a QueueSession object. This QueueSession object should be the same object you use while authoring the JMS message in the JXTAToJMSMsgConverter class. The Router class uses both the JXTAToJMSMsgConverter and JMSSearch classes, so I will explain in the discussion of the Router class below how it ensures that the appropriate QueueSession object is passed to the getQueueSender() method.

The last thing you do in the getQueueSender() method is call the QueueSession object's createSender() method. This method takes the Queue object you just created and returns a QueueSender object. The QueueSender is now complete. If you send your JMS message over this sender object, it automatically reaches the correct recipient. Now, take a look at how the Router class uses all the low-level support you have built to accomplish routing.

The Router class

The Router class is the messaging junction of the JXTA4JMS messaging. The Router class uses the low-level layers (including format conversion and networking) and sends messages from the JMS network to the JXTA network and back. The purpose of the Router class is two-fold:

  • It sends messages received from the JMS network to the mobile clients.
  • It sends messages received from the JXTA network to JMS clients.

Therefore, it contains two methods: sendMessageToMobile() and sendMessageToJMS(). The Listener class calls the sendMessageToMobile() method whenever it receives a message from a JMS client. The sendMessageToMobile() method sends the message to the J2ME client over the JXTA network. Similarly, the Listener class uses the sendMessageToJMS() method whenever it receives a message from a mobile client.

Listing 12 shows the implementation of the Router constructor.


Listing 12. The Router constructor
    public Router ( String peerName, PeerGroup peerGroup ) {
        this.peerName = peerName;
        this.peerGroup = peerGroup;
    }//Router

The implementation of the Router constructor is simple. It just takes two parameters (name of the peer and its peer group) and stores them for later use by the two methods of the Router class.

The sendMessageToMobile() method

Listing 13 shows the implementation of the sendMessageToMobile() method.


Listing 13. The sendMessageToMobile() method
    public void sendMessageToMobile( 
                                   String sender, 
                                   OutputPipe outputPipe, 
                                   TextMessage jmsMessage ) {
        try
        {
            JMSToJXTAMsgConverter jxtaConverter = 
                              new JMSToJXTAMsgConverter ( sender, jmsMessage );
            net.jxta.endpoint.Message jxtaMessage = jxtaConverter.getMessage ();
            outputPipe.send ( jxtaMessage );
        }//try
        catch ( Exception e ) {
            e.printStackTrace ();
        }//catch

    }//sendMessageToMobile()

The sendMessageToMobile() method takes three parameters:

  • The name of the JMS sender whose JMS message you send to the mobile client.

  • An OutputPipe object that represents a JXTA pipe. (The mobile client listens on the other end of this pipe, so you send the message over this pipe.) I explain the mechanics of creating this OutputPipe object later while discussing the Listener class.

  • The JMS message you want to send to the mobile client.

As you can easily guess, all you must do is translate the JMS message from a JMS format to a JXTA format and then send the message over the output pipe.

Therefore, as you just saw in Listing 13's sendMessageToMobile() method, you first instantiate a JMSToJXTAMsgConverter object and use it to get the JXTA representation of the incoming JMS message. Next, you simply call the OutputPipe object's send() method, passing the JMS message along with the method call. The OutputPipe object handles all the low-level processes of sending the message over the JXTA network.

The sendMessageToJMS() method

Now see listing 14, which shows the implementation of the sendMessageToJMS() method.


Listing 14. The sendMessageToJMS() method
    public void sendMessageToJMS( QueueConnectionFactory qConnFactory, 
                                  net.jxta.endpoint.Message jxtaMessage )
    {
        try {
            JXTAToJMSMsgConverter jmsConverter = new JXTAToJMSMsgConverter (
                                                       qConnFactory,
                                                       jxtaMessage
                                                     );

            String jmsRecipient = jmsConverter.getJMSRecipient();
            TextMessage jmsMessage = jmsConverter.getMessage();
            QueueSession queueSession = jmsConverter.getQueueSession();
            
            JMSSearch jmsSearch = new JMSSearch ();

            QueueSender qSender = jmsSearch.getQueueSender ( jmsRecipient,
                                                             queueSession );
            
            qSender.send ( jmsMessage);
         
        }//try
        catch ( Exception e ){
            e.printStackTrace ();
        }//catch
        
    }//sendMessageToJMS

The sendMessageToJMS() method takes two parameters, a QueueConnectionFactory object and the JXTA message you want to send to the JMS recipient. Recall that the JXTAToJMSMessageConverter constructor requires the same two objects. Therefore, the sendMessageToJMS() method instantiates a JXTAToJMSMsgConverter object. It then calls the JXTAToJMSMsgConverter class's getJMSRecepient() and getMessage() methods to fetch the intended JMS message recipient's name and the incoming JXTA message's JMS form, respectively.

The sendMessageToJMS() method also calls the JXTAToJMSMsgConverter class's getQueueSession() method. This method returns a QueueSession object as already explained.

The sendMessageToJMS() method then instantiates a JMSSearch object and calls its getQueueSender() method. The getQueueSender() method takes the name of the intended JMS message recipient (a queue name) and the QueueSession objects as parameters. It returns a QueueSender object (as already explained in the discussion of the JMSSearch class).

Finally, you call the QueueSender object's send() method. The send() method takes the converted JMS message and sends the message to its intended JMS recipient.

The Listener class

The Listener class is designed to act as a continuously listening module. You want to design this class so that after it starts, it keeps listening to both JMS and JXTA networks. As soon as it receives a JMS message, it forwards the message to the J2ME device through the JXTA network. Moreover, as soon as it receives a message from the J2ME device over the JXTA network, it forwards the message to the concerned JMS client.

The Listener class implements a run() method that executes in a separate thread. The thread continuously listens for messages from both the JMS and JXTA networks. I will show how to implement the JMS and the JXTA message processing logic in the run() method. The Listener class contains some private helper methods. These methods assist in creating input and output JXTA pipes.

The createPipeAdvertisement() method

The createPipeAdvertisement() method shown in Listing 15 is a private helper method that creates a pipe advertisement and returns it in the form of a PipeAdvertisement object.


Listing 15. The createPipeAdvertisement() method
    private PipeAdvertisement createPipeAdvertisement (String peerName) {
        PipeAdvertisement pipeAd = null;

        try 
        {
            String fileName = peerName+".xml";
            File file = new File ( fileName );

            if ( file.exists() )
            {
                FileInputStream is = new FileInputStream (file);
                if ( is.available() > 0 )
                {
                    pipeAd =
                       (PipeAdvertisement) AdvertisementFactory.newAdvertisement(
                                           new MimeMediaType( "text/xml" ),
                                           is 
                                         );
                                         
                }//if ( is.available() > 0)
            }
            else
            {
                pipeAd = 
                     (PipeAdvertisement) AdvertisementFactory.newAdvertisement (
                                          pipeAd.getAdvertisementType ()
                                        );
                pipeAd.setName (peerName);
                pipeAd.setType ( "JxtaUnicast" );
                pipeAd.setPipeID( (ID)net.jxta.id.IDFactory.newPipeID(
                                               netPeerGroup.getPeerGroupID()
                                              )
                                            );
                                              
                FileOutputStream os = new FileOutputStream ( fileName );
                os.write ( pipeAd.toString().getBytes() );
                os.close();
            }//end of else        

            return pipeAd;
        
        }//try
        catch (Exception ex) {
            ex.printStackTrace ();
        }//catch

        return null;

    }//createPipeAdvertisement()

Creating a pipe advertisement is just an XML authoring task. You need to author the correct XML code according to the advertisement format defined by the JXTA protocols. Look at Listing 16, which shows the XML structure of a pipe advertisement.


Listing 16. A sample pipe advertisement in XML form
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jxta:PipeAdvertisement>
<jxta:PipeAdvertisement xmlns:jxta="http://jxta.org">
    <Id>
       urn:jxta:uuid-59616261646162614E50.....91E04
    <Id>
    <Type>
       JxtaUnicast
    <Type>
    <Name>
       AliceJMS
    <Name>
</jxta:PipeAdvertisement>

JXTA provides classes for easy authoring of JXTA advertisements. The createPipeAdvertisement() method first checks whether a pipe advertisement already exists (from a previous pipe creation operation) in a file. If the file exists, you simply open the file and read its contents in a file input stream. Next, you use a JXTA class named AdvertisementFactory to create an advertisement from the input stream.

The AdvertisementFactory class has a static method named newAdvertisement() that takes two parameters. The first parameter specifies the MIME type of the advertisement you create. In this case, you want to create an XML advertisement, so the MIME type should be "text/xml." The second parameter specifies the input stream you created from the file.

The newAdvertisement() method returns an Advertisement object that you can cast into a PipeAdvertisement before returning back to the calling application.

Now, take a look at what you do if the pipe advertisement does not exist. Naturally, you must author a pipe advertisement from scratch. For this purpose, you use the overloaded form of the AdvertisementFactory class's newAdvertisement() method. This method takes just one parameter that specifies the type of advertisement you want to create. The newAdvertisement() method returns the pipe advertisement in the form of an Advertisement object. You cast the Advertisement object into a PipeAdvertisement object.

Now you set three parameters in the new pipe advertisement you just created. The three parameters are the name of the pipe you want to advertise, the type of the pipe (JXTAUnicast here), and the pipe identifier. You already know the first two parameters, but you need an identifier factory class named IDFactory to create a new identifier. The factory helps in creating unique identifiers.

After the new advertisement is ready, you save it in the disc file and return the PipeAdvertisement object.

The publishPipeAdvertisement() method

The publishPipeAdvertisement() method, shown in Listing 17, is a private helper method that takes a PipeAdvertisement object and publishes it over the JXTA network.


Listing 17. The publishPipeAdvertisement() method
    private boolean publishPipeAdvertisement (PipeAdvertisement pipeAd)
    {
        DiscoveryService discSvc = netPeerGroup.getDiscoveryService ();
        discSvc.remotePublish ( pipeAd, 
                                DiscoveryService.ADV, 
                                DiscoveryService.NO_EXPIRATION
                              );
        return true;
    }//publishPipeAdvertisement()


You always publish a pipe advertisement in a peer group. Moreover, all advertisements have an expiry time. Therefore, your advertisement appears in a specific peer group for a specified time period.

JXTA provides a service called a pipe service you can use to publish pipe advertisements. The JXTA implementation wraps the functions of pipe service in a class named PipeService. You can obtain an instance of the PipeService class by calling a PeerGroup object's getPipeService() method.

You simply call the PipeService class's remotePublish() method. The remotePublish() method publishes the advertisement on the JXTA network. It takes three parameters (the PipeAdvertisement object you want to publish, the type of advertisement, and the advertisement's expiry time).

The createInputPipe() method

The createInputPipe() method shown in Listing 18 takes a peer name and creates an input pipe to listen for incoming messages.


Listing 18. The createInputPipe() method
    private void createInputPipe (String peerName) {
        PipeAdvertisement pipeAd = createPipeAdvertisement (peerName);
        
        if (pipeAd!= null) {
            boolean published = publishPipeAdvertisement ( pipeAd );
            
            if (published) {
                PipeService pipeSvc = netPeerGroup.getPipeService ();
        
                try {
                    inputPipe = pipeSvc.createInputPipe ( pipeAd );
                    
                }//try
                catch (IOException io) {
                    io.printStackTrace ();
                }//catch
                
            }//if(published)
            
        }//if (pipeAd!= null)
    }//createInputPipe()

The createInputPipe() method creates an input pipe in three steps:

  1. It calls the createPipeAdvertisement() method and passes the peer identifier along with the method call. The createPipeAdvertisement() method returns a PipeAdvertisement object as already discussed.

  2. It then calls the publishPipeAdvertisement() method, passing the PipeAdvertisement object along with the method call. The publishPipeAdvertisement() method publishes the advertisement over the JXTA network.

  3. Finally, it creates an InputPipe object over the published PipeAdvertisement object. For this purpose, you just call the PipeService class's createInputPipe() method, which returns a PipeService object.

The createInputPipe() sets the newly created InputPipe object as a class-level object. You use this JXTA InputPipe object later to listen for incoming JXTA messages.

The searchAndCreateOutputPipe() method

You have seen how to create an input JXTA pipe. Now, look at how to create an output JXTA pipe.

The searchAndCreateOutputPipe() method you see in Listing 19 takes a peer name and creates an output pipe for the particular peer.


Listing 19. The searchAndCreateOutputPipe() method
    private void searchAndCreateOutputPipe(String peerName) {
        try {
            JXTASearch jxtaSearch = new JXTASearch (netPeerGroup);
            PipeAdvertisement pipeAdvert = jxtaSearch.getPipeAdvertisement (peerName);

            if (pipeAdvert != null) 
                outputPipe = jxtaSearch.getOutputPipe(pipeAdvert);
        }//try
        catch ( Exception ex ) {
            ex.printStackTrace ();
        }//catch


    }//searchAndCreateOutputPipe()

The output pipe creation is similar to the input pipe creation process. With input pipe creation, you first create a PipeAdvertisement object and then publish it. But when you create an output pipe object, you start by searching the already published PipeAdvertisement object. After you have the PipeAdvertisement object, the output pipe creation is similar to input pipe creation.

Therefore, output pipe creation consists of two steps: you search the published pipe advertisement and then create the output pipe using the pipe advertisement. You already have the functions for both these tasks in the JXTASearch class.

You call the JXTASearch class's getPipeAdvertisement() method, passing the peer name along with the method call. The getPipeAdvertisement() method returns the PipeAdvertisement object.

In the second step, you call the JXTASearch class's getOutputPipe() method. The getOutputPipe() method takes the PipeAdvertisement and returns an OutputPipe object.

You have seen how the helper methods in the Listener class create input and output pipes. Now, take a look at how the Listener class uses these helper methods.

The Listener constructor

Listing 20 shows the implementation of the Listener constructor:


Listing 20. The Listener constructor
    public Listener ( String userName, 
                      String connectionFactoryName ) {

        try {
            //Preparing for JMS.
            javax.naming.InitialContext jndiContxt = new InitialContext();
            queueConnFactory = 
                 (QueueConnectionFactory) jndiContxt.lookup (connectionFactoryName);
    
            Queue incomingQueue = (Queue) jndiContxt.lookup ("jms/"+userName);
            QueueConnection queueConnection = 
                  queueConnFactory.createQueueConnection ();
            QueueSession queueSession = 
                  queueConnection.createQueueSession ( false, 
                                                       Session.AUTO_ACKNOWLEDGE
                                                     );
            queueReciver = queueSession.createReceiver ( incomingQueue );
            queueConnection.start();

            //Preparing for JXTA.
            netPeerGroup = PeerGroupFactory.newNetPeerGroup ();
            router = new Router( userName, netPeerGroup );
            createInputPipe (userName + "JMS");
            searchAndCreateOutputPipe(userName + "J2ME");
            
        }//try
        catch ( Exception ex ) {
            ex.printStackTrace ();
        }//catch

    }//Listener

The Listener constructor prepares for listening on the JMS and JXTA networks. It takes two parameters: the name of the user and the name of a JMS connection factory. The username specifies the name of the user currently using JXTA4JMS (for example, Alice). This name is the same on both JMS and JXTA networks.

The name of the connection factory indicates which JMS connection factory the Listener class uses to perform search operations on the JMS network. Some higher-level application (such as the simple main() method shown in Listing 21) specifies the two parameters while instantiating the Listener object.


Listing 21. The main() method
    public static void main(String argv[])
    {
        if ( argv.length < 2 )
        {
            usage();
            return;
        }//if ( argv.length < 2)
        
        Listener listener = new Listener ( new String (argv[0]), 
                                           new String (argv[1])
                                          );
        Thread listenerThread = new Thread (listener);
        listenerThread.start();
    }//main()

The Listener constructor first makes its preparations to start listening on the JMS network. For this purpose, it performs the following operations:

  1. The first step is to search for the connection factory using JNDI. Searching for connection factories is the same as searching for a JMS queue (explained in the discussion of the JSMSearch class's getQueueSender() method). You just instantiate an InitialContext object and call its lookup() method.

  2. Now, search for the JMS queue, on which the Listener class will listen. Call it incomingQueue. Note that the outgoingQueue object created earlier in the getQueueSender() method is the one on which you send JXTA messages. The incomingQueue object you search here is the one on which you will listen for incoming JMS messages. This means, for example, if Alice is on the road with her mobile-side JXTA4JMS, the desktop-side JXTA4JMS keeps on listening for incoming JMS messages on her incomingQueue object. Whenever it receives an incoming message (say, from Bob) on incomingQueue, it sends the message to Alice (or her mobile-side JXTA4JMS).

  3. The next step is to use the connection factory (a QueueConnectionFactory object) to instantiate a new JMS connection. A QueueConnection object represents this connection. You can simply call the QueueConnectionFactory object's createQueueConnection() method, which does not take any parameters and returns a QueueConnection object.

  4. Now you use the QueueConnection object to create a JMS queue session. You can create several queue sessions on the same connection. To create a new JMS session, you call the QueueConnection object's createQueueSession() method.

  5. You then use this QueueSession object to create a QueueReceiver object. You call the QueueSession object's createReceiver() method, passing the incomingQueue object along with the method call. The createReceiver() method returns a QueueReceiver object. You use the QueueRecveiver object to receive messages from the incomingQueue.

    Recall from the discussion about the JMSSearch class's getQueueSender() method that you need a QueueSender object to send messages to a queue. QueueSession objects create both QueueSender and QueueReceiver objects.

    Note that you use two different QueueSession objects to create the QueueSender and QueueReceiver objects because you use different queues for sending and receiving messages. At the moment, you create a receiver because you know you must check for incoming messages from only one queue (the queue of the user currently using the JXTA4JMS implementation, which is Alice). On the other hand, the QueueSender you created in the JMSSearch class's createQueueSender() method was for the particular JMS client (Bob) for whom you received the JXTA message.

The Listener constructor is now ready to listen for, or receive, incoming JMS messages from the user's queue. Now let's see how the Listener constructor prepares to receive messages from the JXTA network.

You want to create an input pipe to receive JXTA messages from the mobile client. Similarly, you also create an output pipe to send messages to the mobile client. Note that you already know the names of input and output pipes because the two pipes represent the same user currently using the JXTA4JMS implementation. The input and output pipe names differ in postfix. You allocate postfix "JMS" to the input pipe name because the input pipe represents a desktop-side JXTA4JMS pipe, and the mobile-side JXTA4JMS uses this name to search the desktop-side JXTA4JMS pipe. You allocate postfix "J2ME" to the output pipe because the output pipe represents the mobile-side JXTA4JMS pipe, and the desktop-side JXTA4JMS uses this name to search the mobile-side JXTA4JMS pipe.

The Listener constructor follows these steps to create input and output pipes:

  1. The first step is to instantiate a PeerGroup object. You are working in the default net peer group, so you create a NetPeerGroup object instead of a PeerGroup object. NetPeerGroup is a subclass of the PeerGroup class, so you can use it instead of the PeerGroup class.

  2. Now you can instantiate a Router object by calling its constructor. As previously explained, the Router class's constructor takes the username and a PeerGroup object as parameters.

  3. In step three, you create a JXTA pipe you then publish (or advertise) over the JXTA network. It is an input pipe the J2ME mobile client uses to send messages to the desktop client.

  4. The createInputPipe() method (already discussed) performs all the steps required to create an input pipe. You just call the createInputPipe() method and pass the username to the method.

  5. The last step is to search for and create an output pipe. The desktop client uses this pipe to send messages to the mobile client. To create an output pipe, you can use the searchAndCreateOutputPipe() method, which I discussed earlier.

I have explained all the preparations the Listener constructor performs. Now, see how the Listener class uses these preparations for JXTA4JMS messaging.

The run() method

I have written a run() method in the Listener class that runs as a separate thread, as you can see in Listing 22 .


Listing 22. The run() method
    public void run()
    {
        while( true ) {
            int counter = 0;

            while ( counter <= 10 ) {
                try {
                    javax.jms.Message message = queueReciver.receive (1000);
                    if ( message != null ) {
                        if ( message instanceof TextMessage ) 
                        {
                            TextMessage txtMsg = (TextMessage) message;
                            router.sendMessageToMobile ( 
                                            txtMsg.getStringProperty("Sender"), 
                                            outputPipe, 
                                            txtMsg 
                                            );
                        }
                    }//if ( message != null ) 

                    counter = counter + 1;
                }//try
                catch ( Exception ex ) {
                    ex.printStackTrace ();
                }//catch

            }//while (counter <= 10)

            counter = 0;
            while ( counter <= 5 ) 
            {
                net.jxta.endpoint.Message msg = null;

                try {
                    msg = inputPipe.poll ( 1000 );

                    if ( msg != null )
                        router.sendMessageToJMS( queueConnFactory, msg );

                    counter = counter + 1;
                }//try
                catch ( InterruptedException iEx ) {
                    iEx.printStackTrace ();
                }//catch

            }//while (counter <= 5)

            counter = 0;
            
        }//while(true )

    }//run()

The run() method continuously listens on the JMS and JXTA sides for incoming messages. As soon as it receives an incoming message from a JMS client, it forwards the message to the mobile J2ME client. Similarly, when it receives a message from the mobile client (on the JXTA network) it sends the message to the intended JMS recipient.

The run() method starts with an infinite while loop, inside which I have written two inner while loops. The first loop listens for messages coming from JMS clients, and the second while loop listens for messages from the mobile client. The logic in the loops is simple. It uses the Router class for appropriate routing of messages.


Implement the mobile-side of JXTA4JMS

I described the responsibilities of different J2ME classes in mobile-side JXTA4JMS while discussing JXTA4JMS architecture in The JXTA-for-JMS architecture. Now, I'll examine the implementation details of these classes.

When you implement the J2ME side of JXTA4JMS, you use JXME classes (PeerNetwork, Message and Element). I already described these classes in this series' first article.

The JXTA4JMSMessage class

The JXTA4JMSMessage class authors and processes JXTA4JMS messages. A JXTA4JMS message is a JXME message that can be customized. An application uses the JXTA4JMSMessage class whenever it wants to send or receive a message to or from a desktop peer.

Recall from the "Messaging between a relay and a JXME client" section of the first article that all JXME messages consist of elements. All JXME messages contain certain common elements (for example, the EndpointSourceAddress element discussed in the first article's Listing 3). In addition, a JXME message might contain additional application-specific elements. A JXTA4JMS message contains two of the following three JXTA4JMS-specific elements:

  • The Message element wraps the contents of the message. This element is always present in all JXTA4JMS messages.

  • The JMSSender element wraps the name of the JMS user who sends this message. Naturally, this element is present only in incoming messages received from the desktop-side JXTA4JMS.

  • The JMSRecipient element wraps the name of the JMS user who is the intended JXTA4JMS message recipient. As you can guess, this element is only present in outgoing messages sent to the desktop-side JXTA4JMS.

The JXTA4JMSMessage object might represent either incoming or outgoing messages. The JXTA4JMSMessage class has two constructors, a one-argument constructor and a two-argument constructor. The one-argument constructor helps to process incoming messages, while the two-argument constructor authors outgoing messages. Let me show you how they work.

The one-argument JXTA4JMSMessage constructor

Listing 23 shows the one-argument JXTA4JMSMessage constructor. It takes a JXME Message object as a parameter. This Message object represents an incoming message from the desktop JXTA4JMS client. The Message object contains the Message and JMSSender elements.


Listing 23. The one-argument JXTA4JMSMessage constructor
 
    public JXTA4JMSMessage (Message msg) {
        processMessage(msg);
    }//JXTA4JMSMessage

The constructor simply passes the Message object to a private helper method named processMessage(). The processMessage() method extracts the contents of the Message and JMSSender elements and stores the contents in two class-level variables named messageText and sender, respectively.

Listing 24 shows the processMessage() method; it's similar to the processMessage() method discussed in Listing 19 of the first article, so I won't explain the details here.


Listing 24. The processMessage() method
 
    private void processMessage (Message msg) {
        try {
            for (int j=0; j < msg.getElementCount(); j++) {
                Element e = msg.getElement(j);
                if ((e.getName()).equals("JMSSender"))
                    sender = new String(e.getData());
                else if ((e.getName()).equals("Message"))
                    messageText = new String (e.getData());
                
            }//for (int j=0;) 

            if (sender != null) {
                Element[] msgElements = new Element [2];
                msgElements[0] = new Element ( 
                                              "JMSSender",
                                              (sender).getBytes(),
                                              null,
                                              null 
                                             );

                msgElements[1] = new Element ( 
                                              "Message", 
                                              messageText.getBytes(), 
                                              null, 
                                              null 
                                             );
                          
                message = new Message ( msgElements );    
           }//if (sender!=null)

        }//try
        catch(NumberFormatException ne){
            ne.printStackTrace();
        }//catch
    }//processMessage()

After storing the contents of the two elements, the processMessage() method also authors a Message object corresponding to the contents of the JMSSender and Message elements. I explained the authoring of a JXME message in Listing 26 of the first article.

The two-argument JXTA4JMSMessage constructor

Listing 25 shows the implementation of the two-argument JXTA4JMSMessage constructor. Two strings form the two arguments of the constructor. The first string (recipient) is the name of the JMS recipient, and the second string (messageText) is the actual message you want to send.


Listing 25. The two-argument JXTA4JMSMessage
 
    public JXTA4JMSMessage (String recipient, String messageText) {
       Element[] msgElements = new Element [2];
       this.recipient = recipient;
       this.messageText = messageText;

       msgElements[0] = new Element ( 
                                     "JMSRecipient",
                                     (recipient).getBytes(),
                                     null,
                                     null
                                    );

       msgElements[1] = new Element ( 
                                     "Message", 
                                     messageText.getBytes(), 
                                     null, 
                                     null 
                                     );
                                      
       message = new Message ( msgElements );
    }//JXTA4JMSMessage

The constructor stores the recipient and the messageText strings in two class-level variables named recipient and messageText, respectively. It then authors a Message object corresponding to the recipient and message fields.

The getter methods of the JXTA4JMSMessage class

The JXTA4JMSMessage class also contains four getter methods, as Listing 26 shows:

  • The getMessage() method returns the Message object that either of the two constructors authored. A higher level class (for example, the JXMEMIDlet class) uses this method to get the Message object.

  • The getMessageText() method returns the textual contents of the message.

  • The getRecipient() method returns the name of the outgoing message's JMS recipient (in case the JXTA4JMSMessage object wraps an outgoing message).

  • The getSender() method returns the name of the incoming message's JMS sender (in case the JXTA4JMSMessage object wraps an incoming message).


Listing 26. The four getter methods of the JXTA4JMSMessage class
 
    public Message getMessage(){
        return message;
    }//getMessage()

    public String getSender(){
        return sender;
    }//getSender()
    
    public String getRecipient(){
        return recipient;
    }//getRecipient()

    public String getMessageText() {
        return messageText;
    }//getMessageText()

The DataStore class

The DataStore class provides simple J2ME record store services. The JXTA4JMSMessagingClient class uses the DataStore class's services.

The DataStore constructor (Listing 27) takes the name of a J2ME record store and opens it for read and write operations.


Listing 27. The DataStore constructor
    public DataStore(String storeName) {
        try { 
            recStore = RecordStore.openRecordStore ( storeName, true ); 
        } catch ( Exception e ) {
            e.printStackTrace();
        } 
    }//DataStore

The DataStore class also has a deleteStore() method (shown in Listing 28) that deletes a J2ME record store.


Listing 28. The deleteStore() method
   public void deleteStore(String storeName) {
        try{
            recStore.closeRecordStore();  
            RecordStore.deleteRecordStore(storeName);
        } catch ( Exception e ) { 
            e.printStackTrace();
        } 
    }//deleteStore()

Additionally, the DataStore class has a setRecord() method, shown in Listing 29, that takes two parameters and stores them in a J2ME record store. The two parameters are a pipe identifier and the time when the pipe was first published over the JXTA network. JXME does not help you identify whether a particular pipe has expired, so you must keep track yourself.


Listing 29. The setRecord() method
    public void setRecord (long time, String pipeId)  {
        String record = (Long.toString(time)) +"@"+ pipeId;
        try {
            recStore.addRecord ( record.getBytes(), 0, record.getBytes().length );
            recStore.closeRecordStore();            
        } catch ( Exception e ){
            e.printStackTrace();
        }//catch
                         
    }//setRecord()

The getPipeId() and getAdvPublishTime() methods you see in Listing 30 retrieve the pipe identifier and its publishing time, respectively, from the J2ME record store.


Listing 30. The getPipeId() and getAdvPublishTime() methods
    public String getPipeId() {
        try {
            if ( recStore.getNumRecords() > 0 ) {
                String record = new String( recStore.getRecord (1) );
                int index = record.indexOf("@");
                String pipeId = record.substring (index+1,record.length());
                return pipeId;
            }//if ( recStore.getNumRecords() > 0)
        }//try
        catch (Exception e) {
            e.printStackTrace();
        }//catch
        
        return null;
    }//getPipeId ()

    public long getAdvPublishTime() {
        long timeInMillis = 0L;
        try {
            if ( recStore.getNumRecords() > 0 ) {
                String record = new String( recStore.getRecord (1) );
                int index = record.indexOf("@");
                timeInMillis = Long.parseLong( record.substring (0,index) );
            }
        }//try
        catch (Exception e) {
            e.printStackTrace();
        }//catch

        return timeInMillis;
    }//getAdvPublishTime()

I want to avoid detailed discussion of J2ME record-store programming, so I've provided a link to an excellent developerWorks article in the Resources section. The article demonstrates how to work with J2ME record stores.

The JXTA4JMSMessagingClient class

The JXTA4JMSMessagingClient class is the core of J2ME-side JXTA4JMS. It provides all JXME messaging features to a JXTA4JMS client application. The features include:

  • JXTA relay connection
  • Input and output pipe creation
  • JXTA pipe (where the JXTA4JMS client listens) searching
  • Message sending to the desktop JXTA4JMS client
  • JXTA relay polling for incoming messages

The JXTA4JMSMessagingClient class uses the PeerNetwork class to perform messaging with the relay. The JXTA4JMSMessagingClient also uses the JXTA4JMSMessage class to author and process JXTA4JMS messages.

The JXTA4JMSMessagingClient class also implements the Runnable interface that contains just one method named run(). The run() method always executes in a separate thread. This thread polls the relay to check for incoming messages.

A higher-level application (for example, a MIDlet) specifies the time period after which this thread polls the relay. After polling, the JXTA4JMSMessagingClient class displays the incoming message to the user and then sleeps until the next polling. You will soon see how the JXTA4JMSMessagingClient class implements the polling logic in the run() method.

The JXTA4JMSMessagingClient class consists of a constructor and a method named sendMessage(). In addition, it also contains a few private helper methods to perform low-level JXME messaging. I already covered all JXME-related topics in the first article's "JXME programming" section, so I won't talk about that here.

The JXTA4JMSMessagingClient constructor

Listing 31 shows the JXTA4JMSMessagingClient constructor, which takes four parameters:

  • The relayURL parameter specifies the URL of the relay through which you want to communicate with the JXTA network.

  • The peerName parameter specifies the name of the peer using mobile-side JXTA4JMS (for example, Alice).

  • The timePeriod parameter is an integer that represents a time period in milliseconds. (This is the period between two polls. The JXTA4JMSMessagingClient class polls and then waits for this amount of time before the next poll. The JXTA4JMSMessagingClient class continues doing this.)

  • The TextField parameter is a TextField object that represents a text field on the J2ME device screen. (The JXTA4JMSMessagingClient updates this text field with the incoming message's contents every time it receives a message.)


Listing 31. The JXTA4JMSMessagingClient constructor
    public JXTA4JMSMessagingClient( 
                          String relayURL, 
                          String peerName, 
                          int timePeriod, 
                          TextField textField )
    {
        this.timePeriod = timePeriod;
        this.textField= textField;
        String pipeName = new String(peerName + "J2ME");

        try {
            byte[] persistentState  = null;            
            peerNetwork = PeerNetwork.createInstance ("J2MEClient");
            persistentState = peerNetwork.connect (relayURL, persistentState);
            DataStore dataStore = new DataStore(pipeName);

            if (dataStore.getPipeId() == null) {
                String pipeId = createPipe(pipeName);
                listenToPipe(pipeId);
                dataStore.setRecord( System.currentTimeMillis(), pipeId);
            }//if(dataStore.getPipeId() == null )
            else
            {
                long difference = 
                     (System.currentTimeMillis() - dataStore.getAdvPublishTime());
                                
                if (difference > expiryTime) {
                    dataStore.deleteStore(pipeName);
                    dataStore = new DataStore(pipeName);
                    String pipeId = createPipe(pipeName); 
                    listenToPipe(pipeId);
                    dataStore.setRecord ( System.currentTimeMillis(), pipeId);
                }//if (difference > expiryTime)
                else
                    listenToPipe(dataStore.getPipeId());
            }//else
            outputPipeId = searchPipe (peerName + "JMS");
        }//try
        catch ( Exception e ) {
            e.printStackTrace();
        }//catch
        
    }//JXTA4JMSMessagingClient

The JXTA4JMSMessagingClient must first store the timePeriod and textField parameters as class-level variables. This allows you to use these parameters later when the JXTA4JMSMessagingClient polls the relay for incoming messages.

Then the JXTA4JMSMessagingClient constructor connects to the relay. Your next task is to create an input pipe and ask the relay to start listening to it. The name of the pipe is the username with the postfix "J2ME." (Recall you allocated pipe names earlier during the Listener constructor discussion; the postfix "JMS" is allocated for the pipe on which the desktop client listens, and the postfix "J2ME" is allocated for the pipe on which the J2ME client listens.)

Now check the record store to see whether a valid pipe identifier for the user already exists. If it exists, you can create a pipe with the same pipe identifier. If the pipe identifier does not exist or has expired, request the relay to issue another pipe identifier. After you have the pipe identifier, request the relay to start listening for messages arriving on the pipe.

When you are finished with the input pipe, search for the pipe over which the desktop client creates and listens for messages coming from the J2ME client (the name of the pipe is the username with a postfix "JMS"). For the J2ME client, this is an output pipe.

Recall that I discussed how to request an identifier, search for pipe, and request the relay to start listening on a pipe in the first article's "JXME programming" section. Three helper methods (searchPipe(), createPipe(), and listenToPipe(), shown in Listing 32) in the JXTA4JMSMessagingClient class, perform these tasks.


Listing 32. The searchPipe(), createPipe(), and listenToPipe() helper methods
    private String searchPipe(String pipeName) {
        String pipeId = null;    
        try {
            int messageID = peerNetwork.search (
                                        PeerNetwork.PIPE,
                                        "Name", 
                                        pipeName, 
                                        -1);
            Message msg = null;
            
            do {
                msg = peerNetwork.poll(5000);
                if (msg != null) {
                    pipeId = processMessage(msg, messageID, "result");
                    return pipeId;
                }//if (msg != null)
            } while (msg!=null);
        }//try            
        catch (IOException ie) { 
            ie.printStackTrace();
        }//catch

        return null;
    }//searchPipe()

    private String createPipe(String pipeName) {
        String pipeId = null;    
        try {
            int messageID = peerNetwork.create (
                                     PeerNetwork.PIPE, 
                                     pipeName, null, 
                                     PeerNetwork.UNICAST_PIPE);
            Message  msg = peerNetwork.poll(5000);

            if (msg == null)
                return null;
            
            pipeId = processMessage(msg, messageID, "success");
        }//try 
        catch (IOException ie) {
            ie.printStackTrace ();
        }//catch

        return pipeId;
    }//createPipe()

    private String listenToPipe (String pipeId) {
        String listenOk = null;
        try {
            int messageID = peerNetwork.listen (pipeId);
            Message msg = peerNetwork.poll(5000);

            if (msg == null)
                return null;

            listenOk = processMessage (msg, messageID, "success");
        }//try 
        catch (IOException ie) {
            ie.printStackTrace ();
        }//catch
        
        return listenOk;
    }//listenToPipe()

The sendMessage() method

The sendMessage() method in Listing 33 below takes a JXTA4JMSMessage object as a parameter and sends the message to the desktop peer over the output pipe created in the JXAT4JMSMessagingClient constructor.

The ultimate recipient of this JXTA4JMSMessage is a JMS user, whose information is stored in the JXTA4JMSMessage object. As described in Messaging from a JMS client to a J2ME client, the message first reaches the relay, where it's translated to JXTA format; the relay then forwards the message to the desktop JXTA4JMS client, which translates the message to JMS format; the desktop JXTA4JMS client finally sends the message to the JMS queue of the ultimate recipient.


Listing 33. The sendMessage() method
    public void sendMessage (JXTA4JMSMessage message) {
        try {
            int messageID = peerNetwork.send (outputPipeId, message.getMessage());
            Message  msg = peerNetwork.poll(5000);

            if (msg != null)
                processMessage(msg, messageID, "success");

        }//try
        catch (IOException ie){
            ie.printStackTrace();
        }//catch
    }//sendMessage()

The run() method

The JXTA4JMSMessagingClient implements the Runnable interface that consists of just one method named run(). This method periodically polls the relay to check for any incoming messages.

The run() method implementation (shown in Listing 34) is simple. It polls the relay and then sleeps for the time period specified by the timePeriod parameter to the JXTA4JMSMessagingClient constructor. When the sleep time finishes, it polls the relay again.

When it receives a message as a result of polling, it uses the JXTA4JMSMessage class to process the message and extract the contents of the message. Finally, it displays the incoming message to the user on the textField object that was passed as the last parameter to the JXTA4JMSMessagingClient constructor.

The run() method executes in a separate thread. This means that the polling mechanism runs independently of sending messages.


Listing 34. The run() method
    public void run() {
        Message msg = null;
        String message = null;
        JXTA4JMSMessage jxmeMsg = null; 
        stringBuffer = new StringBuffer();

        while( true ) {
            try {
                Thread.sleep(timePeriod);
                msg = peerNetwork.poll (2000);
                if (msg != null) {
                    jxmeMsg = new JXTA4JMSMessage (msg);
                    if ((jxmeMsg.getMessage()!=null) &&
                                   (jxmeMsg.getSender()!=null))
                    {
                        textField.setLabel("Message From: "+jxmeMsg.getSender());
                        textField.setString(jxmeMsg.getMessageText());
                    }
                }//if(msg != null)
            }//try
            catch (Exception e) {
                e.printStackTrace();
            }//catch
        }//while(true)
    }//run()

The JXMEMIDlet class

I included a sample MIDlet called JXMEMIDlet in this article's source code download. The sample MIDlet demonstrates the simple use of mobile-side JXTA4JMS.

Listing 35 shows the JXMEMIDlet constructor that instantiates a JXTA4JMSMessagingClient object passing hard-coded values of the peer name, the address of the relay, the time interval between polls, and a text area object to the JXTA4JMSMessagingClient constructor.


Listing 35. The JXMEMIDlet constructor
    public JXMEMIDlet() {
        try {
            tfMessageFromSender = new TextField ( "", "", 40, TextField.UNEDITABLE);
            jxmeMessagingClient = new JXTA4JMSMessagingClient(
                                         relayURL, 
                                         "Alice", 
                                         15000, 
                                         tfMessageFromSender);
           showMessagingForm();
        }//try
        catch ( Exception e ){
            e.printStackTrace();
        }//catch
        
    }//JXMEMIDlet

The startApp() method you see in Listing 36 of JXMEMIDlet starts the polling thread by instantiating a Thread object and then calling the Thread.start() method.


Listing 36. The startApp() method
    public void startApp(){
        Thread thread = new Thread (jxmeMessagingClient );
        thread.start();
    }//startApp()

Also look at the commandAction() method of JXMEMIDlet in Listing 37, which shows how to use JXTA4JMS to send messages to the JMS network.


Listing 37. The commandAction() method
    public void commandAction (Command c, Item item) {
        if ( c == CMD_SEND ) {
            try {
                if (tfRecipientName.getString().equals( "" )) {
                    Alert alert = new Alert(
                          "Error", 
                          "Please enter recipient name for sending message", 
                          null, 
                          AlertType.ERROR
                          );
                    alert.setTimeout( 1000 );
                    display.setCurrent ((Screen) alert);
                
                }//if (tfRecipientName.getString().equals( "" ))
                else {
                   JXTA4JMSMessage message = new JXTA4JMSMessage( 
                                               (tfRecipientName.getString()),
                                               tfMessageForRecipient.getString());
                   jxmeMessagingClient.sendMessage (message);
                }
            }//try
            catch ( Exception e ) {
                e.printStackTrace();
            }
        }//if (c == CMD_SEND)
    }//commandAction()

Figure 8 shows a screenshot of the message receiving and authoring the user interface. When JXTA4JMS receives an incoming message through the polling thread, the name of the sender (for example, Bob) appears in the "Message From:" field. The contents of the incoming message appear in the next line.

When Alice wants to send a message to a JMS user (for example, Bob), she types the name of the JMS user in the "Message To:" field, types the message in the "Message" field, and then presses Send. JXTA4JMS then carries the message to the JMS recipient.


Figure 8. The message receiving and authoring the user interface
The message receiving and authoring the user interface.

To make it easy to try out JXTA4JMS, I wrote a simple JMS test client application and included it in this article's source code download. The JMS client application can represent a JMS user. The readme file in the source code download explains how to test the communication between Alice and Bob using JXTA4JMS.


In conclusion

This article discussed the architecture of JXTA4JMS. It also explained the JXTA4JMS implementation and demonstrated how JXTA4JMS can provide wireless connectivity to JMS users.



Download

NameSizeDownload method
wi-jxta2source.zipHTTP

Information about download methods


Resources

About the author

Faheem Khan is an independent software consultant specializing in Enterprise Application Integration (EAI) and B2B solutions. He can be reached at fkhan872@yahoo.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in

If you don't have an IBM ID and password, register here.


Forgot your IBM ID?


Forgot your password?
Change your password


By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)


By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=86344
ArticleTitle=Wireless messaging with JXTA, Part 2: Implement JXTA-for-JMS
publish-date=02222005
author1-email=fkhan872@yahoo.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).