Track wireless sessions with J2ME/MIDP

Learn three ways to maintain client state information in mobile commerce applications

1 2 3 Page 2
Page 2 of 3

Following the examples, you can associate each cookie with more properties, such as URL path and expiration time, in real-world applications when such needs arise.

Run the example

Please refer to "Deploy the Example MIDP Application on Palm OS Devices," a sidebar from our previous JavaWorld article, for more information on how to build the example programs from the sample code accompanying this article.

Once you set up the application server and MIDP VM properly, you can easily run the example program. Figure 2 shows the initial screen of the MIDlet CookieFetch.

Figure 2. Input a URL for cookie-based session tracking

Put in our server program's URL and press the Fetch button. A test server URL is the above-mentioned JSP counter, CookieSession.jsp. The server returns the number of times you visited the URL during this current session, as Figure 3 illustrates.

Figure 3. The server page tracks the number of visits

For a lightweight general-purpose library devoted to cookie handling on the device, read "A Recipe for Cookie Management," Sonal Bansal (JavaWorld, April 2002).

URL rewriting

Cookies prove easy to use and provide a standard way for session tracking. However, due to some previous cookie abuses, most perceive cookies as unsafe for privacy. As a result, some people hesitate to use cookie-enabled applications. Privacy proves a specific concern for handheld devices because many people use PDAs to manage sensitive personal and financial information. To address this concern, most browsers and servers now have options to turn off cookie support completely.

For cookie-disabled applications, we must use alternative methods to track sessions. One standard failover method for cookies is URL rewriting, which most Web servers support, including J2EE servlet containers. As its name suggests, the URL-rewriting technique embeds session identification information in custom-formatted URLs.

Server side

In the URL-rewriting scheme, when the server encounters a connection that does not belong to any existing sessions, it establishes a new session and generates a unique string that attaches to the end of all URLs associated with subsequent connection requests. To avoid clashes with legitimate URL paths and GET parameters, the attached identification string takes a format resembling the following:

;jsessionid=F953068341A94947242185974585D007

The server detects all URLs with such attachments and groups them into appropriate sessions for future requests.

In a Java servlet container, to rewrite a URL, you pass the desired URL to the HttpSession.encodeURL() method. As you learned earlier, an HttpSession object can maintain sessions through cookies. It can also support URL rewriting with its internal session identification information. If we pass an empty string to HttpSession.encodeURL(), it returns the URL attachment associated with the current session. Then, the server sends the URL attachment back to the MIDP client through a custom HTTP header. We use the HTTP header url-attmt to pass the URL attachment. The snippet below from URLRewriteSession.jsp illustrates the server actions we discuss above:

// Get an existing session or start a new session.
HttpSession sess = request.getSession(true);
 
// Rewrite an empty URL to get the URL attachment.
String URLAttachment = response.encodeURL( "" );
// Set the response header "url-attmt"
// so that the client can know the URLAttachment.
response.addHeader("url-attmt", URLAttachment);

Client side

When the client receives the URL attachment from the url-attmt header, it must store the attachment for future use. The client stores the URL attachment as a static variable in the URLRewriteConnector class. URLRewriteConnector's structure resembles the RMSCookieConnector class. If you need stronger persistence, you can put the URL attachment in an RMS record store and even associate it with a host name as we did for cookies. The code to retrieve a URL attachment from a server response works the same way as the code in RMSCookieConnector:

static void getURLAttachment(HttpConnection c) throws IOException {
  // Iterate through headers and get the first "url-attmt" value.
  int k = 0;
  while (c.getHeaderFieldKey(k) != null) {
    String key = c.getHeaderFieldKey(k);
    String value = c.getHeaderField(k);
    if (key.equals("url-attmt")) {
      URLAttachment = value;
      break;
    }
    k++;
  }
}

With a valid URL attachment, URLRewriteConnector can open new session-aware connections. URLRewriteConnector.open() rewrites the desired URL and then passes it to the Connector.open() method to get a new connection object. URLRewriteConnector then returns a wrapper object HttpURLRewriteConnection around the new connection object. HttpURLRewriteConnection implements the HttpConnection interface through the Decorator pattern. The following code segment illustrates the above steps in the URLRewriteConnector.open() method:

public static HttpConnection open(String url) throws IOException {
  if ( URLAttachment == null ) {
   // Do nothing.
  } else {
    url = url + URLAttachment;
  }
  HttpConnection c = (HttpConnection) Connector.open(url);
  HttpConnection sc = new HttpURLRewriteConnection(c);
  return sc;
}

Run the example

Although tracking sessions using the URL-rewriting technique differs from the cookie technique, their implementations resemble each other. Similar to the cookie approach, with the URL-rewriting approach, we buried the real work in decorator classes to provide transparent session-aware HTTP network connections to applications. Therefore, it should not surprise you that MIDlet URLRewriteFetch resembles MIDlet CookieFetch. The only difference: URLRewriteFetch uses URLRewriteConnector to open new connections, while CookieFetch uses RMSCookieConnector.

Figures 4 and 5 demonstrate the URL-rewriting technique in MIDlet URLRewriteFetch and JSP URLRewriteSession.jsp.

Figure 4. Input a URL for URL rewriting-based session tracking
Figure 5. The server page tracks the visit number

Embed complex client state information in XML directly

Session tracking using cookies and URL-rewriting techniques embed session information inside HTTP headers or URLs. However, since the headers or URLs have limited space for embedding complex information, that session-tracking information is usually small pieces of identification strings. In those solutions, the server maintains the real session state information in data objects associated with those identification strings.

However, what if we want richer state information on the client side? We could embed rich session information directly in the content exchanged by HTTP connections.

The world of HTML browsers doesn't widely employ this technique—except, perhaps, in hidden fields embedded in HTML forms. Traditional browsers usually require a form to submit content data to the server, and the HTML language itself is designed to describe visual display elements for human beings.

But in the world of MIDP wireless applications, MIDlets handle the visual display UI (user interface) elements. You can optimize the communication between MIDlets and the backend servers to best express application logic using a most efficient XML format. Clearly part of the application logic, session information should be incorporated into the XML communication protocol.

Session information envelope for XML documents

You can add session information to XML documents in two ways: You can place the XML elements containing session information anywhere inside the original XML document (embedded session elements). Or you can place the original document inside a wrapper document, which also contains the session information elements (enveloping session elements).

To keep the original document intact and the parsing simple, we decided to use enveloping session elements. As mentioned above, the main advantage of expressing session information in XML elements is that the client can maintain rich state information. However, to keep things simple, in our example, we exchange only one SessionID element between the client and server. Our XML document takes the following format:

<SessionWrapper>
  <SessionID id="the session id" />
  <OriginalContent>
    <!-- Original content go here -->
  </OriginalContent>
</SessionWrapper>

Server side

On the server side, the persistent data objects containing session state information are stored with SessionIDs as attribute pairs in the PageContext object. Those pairs have the application scope, which means they can be retrieved as long as the server is running—unless a servlet invalidates them.

If the server receives an XML document with an empty SessionID, it generates a new one according to the current time in milliseconds and embeds it in the return XML document.

Below is the code snippet from our XMLSession.jsp; it illustrates the server-side actions above:

// Counter object to be stored with the session.
String count;
// ... ...
// Get session ID sid from the request. That requires some XML parsing
// which will be discussed in the next section.
// ... ...
// Get the "count" attribute associated with the sessionID.
Object o = pageContext.getAttribute(sid, PageContext.APPLICATION_SCOPE);
// If the sessionID is not found,
// set count to zero and start a new session.
if ( o == null ) {
  sid = Long.toString( (new Date()).getTime() );
  count = "0";
  pageContext.setAttribute(sid, count, PageContext.APPLICATION_SCOPE);
} else {
  count = (String) o;
}
// Increase count by one in every visit.
count = Integer.toString(Integer.parseInt(count)+1);
pageContext.setAttribute(sid, count, PageContext.APPLICATION_SCOPE);

Client side

In this section, we discuss how to handle the extra XML session elements on the client side. The MIDP client relies on help class XMLSessionWrapper to preprocess XML contents to be posted to the server and post-process XML contents received from the server. The session identification information is stored as a static data member in the XMLSessionWrapper class.

XMLSessionWrapper.unwrapDocument() uses the kXML lightweight CLDC (Connected Limited Device Configuration)-compatible parser to parse the incoming document into a kDOM (Document Object Model) object and then retrieves the session identification string (SessionID). It returns the unenveloped original XML node for future processing. The following code segment illustrates this process:

// Unwrap an input stream "xmlStream",
// which contains a sessionID wrapper around the original data.
// Return a Node representing the unwrapped data.
public static Node unwrapDocument(InputStream xmlStream)
                                           throws Exception {
  InputStreamReader reader = new InputStreamReader(xmlStream);
  XmlParser parser = new XmlParser (reader);
  Document doc = new Document();
  doc.parse(parser);
 
  // Root element of the wrapped XML document.
  Element wrapper = doc.getRootElement();
  // The first element under root contains session info.
  Element sessionInfo = (Element) wrapper.getChild(0);
  // The second element under root is the original XML document.
  Element originalContent = (Element) wrapper.getChild(1);
 
  // Set static variable sessionID.
  sessionID = sessionInfo.getAttribute("id").getValue();
 
  // The unwrapped original content is a node under OriginalContent.
  return (Node) originalContent.getChild(0);
}

The XMLSessionWrapper.wrapDocument() method envelops any XML string with SessionID information directly through string manipulations. The following code segment shows the source for the wrapDocument() method:

// Embed sessionID into a wrapper XML document around xmlDoc
// and return the wrapper XML document.
public static String wrapDocument(String xmlDoc) {
  String wrapID;
  if ( sessionID == null ) {
    wrapID = "";
  } else {
    wrapID = sessionID;
  }
  return "<SessionWrapper><SessionID id=\"" +
         wrapID +
         "\"/>" + xmlDoc + "<OriginalContent>" +
         "</OriginalContent></SessionWrapper>";
}

Figures 6 and 7 show the XML-based session tracking with MIDlet XMLFetch.java and JSP XMLSession.jsp in action.

Figure 6. Input a URL for XML-based session tracking. This technique requires close cooperation between the server and the client.
Figure 7. The server page tracks the visit number and returns an XML element containing that information

The right track

In this article, we reviewed three HTTP session-tracking methods for MIDP applications and provided implementation frameworks. The techniques allow us to maintain client state information over the stateless HTTP protocol and therefore prove crucial to enterprise-level MIDP applications.

Our three approaches have different levels of flexibility, power, and transparency. You could use our sample code for the cookie and URL-rewriting techniques directly to add simple out-of-the-box HTTP session support for MIDP applications. We intended our sample code for XML-enveloping session support to show how to exchange rich session state information maintained on the client side. You could adapt that method to tailor each project's individual needs.

Michael Yuan and Ju Long are PhD candidates at the University of Texas at Austin. They use J2ME and J2EE to develop mobile research applications for projects in the Center for Research in Electronic Commerce.
1 2 3 Page 2
Page 2 of 3