IBM Skip to main content
Search for:   within 
      Search help  
     IBM home  |  Products & services  |  Support & downloads   |  My account

developerWorks > Java technology | Wireless
developerWorks
J2ME 101, Part 4: The Generic Connection Framework
639 KBe-mail it!
Contents:
What is GCF?
Connection protocol support
Opening a connection
The DownloadImage MIDlet
HTTP support in MIDP
Request and response protocols
The HttpConnection API
The FileViewer MIDlet
Accessing a Java servlet
Calling a servlet
Parsing servlet results
Conclusion
Resources
About the author
Rate this article
Related content:
J2ME wireless messaging
Networking with J2ME
Sprites in J2ME
WebSphere Micro Environment
The IBM Developer Kits for the Java platform (downloads)
Subscriptions:
dW newsletters
dW Subscription
(CDs and downloads)
Network support in J2ME/MIDP

Level: Introductory

John Muchow (mailto:John@CoreJ2ME.com?cc=&subject=The Generic Connection Framework)
Developer and Author
13 January 2004

The Generic Connection Framework (GCF) provides an extensible, generic I/O framework for resource constrained devices. In this final installment in the J2ME 101 series, author John Muchow walks you through the GCF interfaces, showing you how they facilitate the development and support of various types of network and file I/O on MIDP.

This article is the final piece in the four-part J2ME 101 series, which consists of a two-part tutorial and two follow-up articles. In this last installment, we'll explore network communication on MIDP, using the Generic Connection Framework (GCF).

As in the previous content in this series, we'll start with the basics (in this case, how to open a connection to a remote resource), but quickly move into more complex territory. Our first MIDlet will demonstrate how to download and display an image that is stored on a server; our second one will use an HTTP connection to download and display a text file from a server; and our final MIDlet will get you started with passing date and time parameters to a servlet.

Please note that this article assumes that you are familiar with MIDlet development in the J2ME environment. You will need to have a J2ME development environment installed on your system in order to compile the code examples. See the Resources section for links to the first three installments in the J2ME 101 series, as well as installation instructions for the J2ME Wireless Toolkit (WTK).

What is GCF?
GCF is a set of interfaces defined within the javax.microedition.io package. Figure 1 shows the GCF class hierarchy.

Figure 1. Class hierarchy of the Generic Connection Framework
Diagram of the class hierarchy for Generic Connection Framework

A total of seven interfaces are defined in GCF, with Connection at the root. Notice that both datagram (packet) and stream connections are supported. As you would assume, working your way down the hierarchy you'll find interfaces that provide additional functionality. For example, StreamConnection supports both input and output streams, and ContentConnection extends StreamConnection with support for determining the content type, data length, and encoding format of a stream.

Don't miss the rest of this series!
Part 1: Introduction to MIDP's high-level interface (November 2003) provides a step-by-step introduction to the components that facilitate the main interaction between the user and the device display.

Part 2: Introduction to MIDP's low-level interface (December 2003) walks through the basics of creating and working with the Canvas and Graphics classes, and provides a brief overview of the Game API, introduced with MIDP 2.0.

Part 3: Inside the Record Management System (December 2003) introduces the basics of the RMS application interface, then walks you through several development examples that illustrate its functionality.

The Connector class is used to open every type of connection in GCF. Here you can see the format for the open() method inside the Connector class:

Connector.Open("protocol:address;parameters");

Connection protocol support
GCF is exceptionally flexible in its support for different connection protocols. When a request is made to open a connection, the Connector class uses its Class.forName() method to search for a class that implements the requested protocol. If found, an object is returned that implements the Connection interface, as shown in Figure 1.

In Listing 1, you can see the code to open various connection types.

Listing 1. Opening various connection types

Connector.Open("socket://www.corej2me.com.com:55");
Connector.Open("http://www.corej2me.com");
Connector.Open("datagram://www.corej2me.com:1000");
Connector.Open("file://makefile.txt");

Opening a connection
GCF has a total of seven methods for creating a connection. All of them are shown in Listing 2.

Listing 2. Seven methods to create a connection

Connector  (public class Connector)
   public static Connection open(String name)
   public static Connection open(String name)
   public static Connection open(String name, int mode, boolean timeouts)
   public static DataInputStream openDataInputStream(String name)
   public static DataOutputStream openDataOutputStream(String name)
   public static InputStream openInputStream(String name)
   public static OutputStream openOutputStream(String name)

Listing 3 illustrates one way to open a connection and read data through a stream.

Listing 3. One way to open a connection

// Create a ContentConnection
String url = "http://www.corej2me.com"
ContentConnection connection = (ContentConnection) Connector.open(url);

// With the connection, open a stream
InputStream iStrm = connection.openInputStream();

// ContentConnection includes a length method
int length = (int) connection.getLength();
if (length != -1)
{
  byte imageData[] = new byte[length];

  // Read the data into an array
  iStrm.read(imageData);
}

The ContentConnection class isn't the only option we have for creating a connection. We could also choose to create an InputStream directly. In Listing 4, you can see what might happen if we needed to download an image over a network connection and create an Image based on the downloaded contents.

Listing 4. Creating an input stream directly

InputStream iStrm = (InputStream) Connector.openInputStream(url);
Image img = null;

try
{
  ByteArrayOutputStream bStrm = new ByteArrayOutputStream();

  int ch;
  while ((ch = iStrm.read()) != -1)
    bStrm.write(ch);

  // Place into image array
  byte imageData[] = bStrm.toByteArray();

  // Create the image from the byte array
  img = Image.createImage(imageData, 0, imageData.length);
}
finally
{
  // Clean up
  if (iStrm != null)
    iStrm.close();
}

As you may have noticed, bypassing the ContentConnection leaves us no method to determine the length of the incoming data. This isn't much of a problem, however, because we can use the ByteArrayOutputStream to read and transfer the data to our destination array.

Image support
Portable Network Graphics (PNG) is the only image format required for any MIDP device implementation. See Resources to learn more about the PNG format.

The DownloadImage MIDlet
Let's write a short MIDlet that builds on the code shown in Listing 4. The DownloadImage MIDlet will demonstrate the steps to download and display an image in an MIDP application. The MIDlet will use a ByteArrayOutputStream to download the remote data and then display the resulting image on a Form using an ImageItem.

Take a look at the complete code for the DownloadImage MIDlet and then we'll discuss it in more detail.

Once the MIDlet is running the main user interface should appear in your WTK device emulator, as shown in the figures below. Figure 2 shows a TextBox that prompts for the URL of image. Clicking the View command initiates the download.

Figure 2. A textbox displaying a URL prompt
Screenshot of a textbox displaying a URL prompt

Once the image is received it is displayed on the device as shown in Figure 3.

Figure 3. An example image display screen
Screenshot of an example image display screen

HTTP support in MIDP
Now that you've seen how GCF supports various types of connections and we've developed our first networking MIDlet, it's time to take a closer look at HTTP support in MIDP. We'll start with an updated hierarchy diagram that indicates which class provides support for HTTP connections.

Figure 4. GCF class support for HTTP
Diagram of the GCF class hierarchy diagram showing support for HTTP

The original MIDP 1.0 specification only required that devices support the HTTP connection protocol, whereas the more recent MIDP 2.0 spec requires support for both HTTP and HTTPS, with the latter offering support for more secure network connections. The APIs to work with these protocols are HttpConnection and HttpConnections, respectively. In addition to these mandated protocols, a device manufacturer can choose to support additional communication protocols such as datagrams or sockets. While at times convenient, you should know that using vendor-specific protocols could affect your application's portability to other devices.

Request and response protocols
Both HTTP and HTTPS are request/response protocols. A client sends a request and a server sends a response. We'll look at the stages of both the client request and the server response before we move on.

Client request
The client request, sometimes called the request entity, consists of the following three sections:

  • Request method
  • Header
  • Body

We'll look at each of these sections in some detail.

Request method

The request method determines how data will be sent to a remote resource. The three methods available are GET, POST, and HEADER. When using GET, data is sent as part of the URL. With POST, any client data is sent in a separate stream, distinct from the request to establish a connection. HEADER requests do not send any data to a server. Instead, HEADER requests only meta information about the remote resource.

Listing 5 shows how we would open an HTTP connection with a specified request method of GET, passing a parameter with the name size and a value of large.

Listing 5. Opening an HTTP connection with GET

String url = "http://www.corej2me.com?size=large";

HttpConnection http = null;
http = (HttpConnection) Connector.open(url);
http.setRequestMethod(HttpConnection.GET);

Header

Header fields let us pass parameters, if you will, from the client to the server. Common fields are If-Modified-Since, Accept, and User Agent. You set header fields as key-value pairs, using the setRequestProperty() method. Listing 6 shows a header request specifying that only data that has been modified since November 11, 2003 be sent back from the server.

Listing 6. A typical header request

String url = "http://www.corej2me.com\somefile.txt";

HttpConnection http = null;
http = (HttpConnection) Connector.open(url);

http.setRequestMethod(HttpConnection.GET);

// Set header field as key-value pair
http.setRequestProperty("If-Modified-Since", "Mon, 12 Jan 2004 12:00:00 GMT");

Body

The body contains the actual content that you would like to send from the client to the server. For instance, Listing 7 shows how to specify the POST request method and send client data over a stream.

Listing 7. Sending data over a stream

String url = "http://www.corej2me.com",
       tmp = "test data here";

OutputStream ostrm = null;

HttpConnection http = null;
http = (HttpConnection) Connector.open(url);
http.setRequestMethod(HttpConnection.POST);

// Send client body
ostrm = http.openOutputStream();
byte bytes[] = tmp.getBytes();
for(int i = 0; i < bytes.length; i++)
{
  os.write(bytes[i]);
}
os.flush();

Oftentimes you will not need to specify any additional information beyond the request method and header fields. If you do need to specify additional information you will include the data in the body.

GET versus POST
GET and POST are two similar request methods that yield different results. When a GET request is used, the data will be sent as part of a URL (via URL encoding), which poses a limit on the amount of data that can be transferred. When we use a request method of POST, the data is sent over a stream, so there is no limit on the amount of data that can be retrieved.

Server response
After the server has received and processed the client request, it must package and send a response. As with the client request, three sections are associated with the server response:

  • Status line
  • Header
  • Body

Status line

As the name implies, the server status line informs the client of the outcome of its request. HTTP classifies the status line codes into the following broad categories:

  • 1xx is informational
  • 2xx is success
  • 3xx is redirection
  • 4xx is client error
  • 5xx is server error

The server status line includes the protocol version number running on the server, the status code, and a text message representing the return code. Below are several examples of valid status lines:

  • "HTTP/1.1 200 OK"
  • "HTTP/1.1 400 Bad Request"
  • "HTTP/1.1 500 Internal Server Error"

Header

Not unlike the client, the server can send information through header fields. Three of the most common methods for retrieving header information sent from a server are shown in Listing 8.

Listing 8. Three common getHeaderField methods

String getHeaderField(int n) Get header field value looking up by index
String getHeaderField(String name) Get header field value 
looking up by name
String getHeaderFieldKey(int n) Get header field key using index

A server could potentially return more than one header field. In this case, the first method would obtain the value of the header field by specifying its index in the result set and the next one would retrieve a header field value by looking up its key by name. Should you need to get the header field key, you could pass a parameter to the final method representing the index of the entry in the result set you were interested in.

Custom header fields
If the header fields defined in the HTTP specification aren't sufficient to your needs, you can also retrieve custom headers, which you define and return from the server. Custom headers are commonly used for session management when using URL-rewriting.

Table 1 shows what each of the three methods in Listing 8 would return if the response in a server header contained the content "content-type=text/plain". Assume for this example that the server results set contains only one entry.

Table 1. Retrieving header field contents
Method Return value
http.getHeaderField(0) "text-plain"
http.getHeaderField("content-type") "text-plain"
http.getHeaderFieldKey(0) "content-type"

Body

Like the client, the server sends the bulk of information in the body of its response. Along similar lines as Listing 7 (which shows a client sending its body through an output stream), the client would read the server response using an input stream.

The HttpConnection API
As previously mentioned, we use the HttpConnection API to establish connections on MIDP. Table 2 shows all the methods available in the HttpConnection class.

Table 2. All the HttpConnection methods
Method Description
long getDate() Get header field date
long getExpiration() Gets header field expiration
String getFile()> Gets filename from the URL
int getHeaderField(int n) Gets header field value looking up by index
String getHeaderField(String name) Gets header field value looking up by name
long getHeaderFieldDate(String name, long def) Gets named field as a long (representing the date)
int getHeaderFieldInt(String name, int def) Gets named field as an integer
String getHeaderFieldKey(int n) Gets header field key using index
String getHost() Gets host from the URL
long getLastModified() Gets last-modified field value
String getPort() Gets port from the URL
String getProtocol() Gets protocol from the URL
String getQuery() Gets the query string (only valid with GET request)
String getRef() Gets the reference portion of URL
String getRequestMethod() Gets the current setting of the request method (GET, POST or HEAD)
String getRequestProperty(String key) Gets the current setting of a request property
int getResponseCode() Gets the response code (numeric value)
String getResponseMessage() Gets the response message (text value)
String getURL() Gets the entire URL
void setRequestMethod(String method) Sets the request method (GET, POST or HEAD)
void setRequestProperty(String key, String value) Sets a request property (header information)

The FileViewer MIDlet
Our next MIDlet will use an HttpConnection to download and view the contents of a text file. This short example will give you some insight into how we send client information and interpret a server response. The MIDlet also includes calls to various methods within the HttpConnection class, which help us to gather information about the host server, the port, and the content type returned.

Take a look at the complete code for the FileViewer MIDlet and then we'll discuss it in more detail.

Now, let's view the output of our MIDlet. The left screen shot in Figure 5 shows the interface upon startup. After you select the View command, the file will be downloaded and displayed on the device, as shown on the right screen shot in Figure 5.

Figure 5. Starting up
A screenshot of the FileViewer MIDlet's startup screen

Figure 6 shows the fields returned from the server, with the values written to the console.

Figure 6. FileViewer's console output
A screenshot of FileViewer's console output

Note that the status line (message and code) and all the header field key-value pairs are displayed. HttpConnection also includes the methods shown in Listing 9 to obtain server information, including the host, port, and content type returned. These values for the FileViewer MIDlet are shown near the bottom of Figure 6.

Listing 9. Methods to obtain server information

String getHost()
String getPort()
String getType()

Accessing a Java servlet
We're going to develop one last MIDlet together before we close the J2ME 101 series. The DateFormat MIDlet (see the complete source code) will demonstrate the steps necessary to access a Java servlet from within a MIDlet. This is a fairly complex operation compared to the others you've learned so far, but we'll walk through it together in the sections that follow.

We'll start by passing parameters to a servlet requesting a specified date and time format to be returned. The servlet will create a properly formatted string based on the parameters and send the result to the client, where it will be displayed on the WTK device emulator. Table 3 lists the available parameters and their meanings, along with a brief example.

Table 3. Servlet parameters
Symbol Meaning Example
G Era designator AD
y Year 1996
M Month in year 07
d Day in month 10
h Hour in am/pm (1~12) 12
H Hour in day (0~23) 0
m Minute in hour 30
s Second in minute 55
S Millisecond 978
E Day in week Tuesday
D Day in year 189
F Day of week in month 2 (2nd Wed in July)
w Week in year 27
W Week in month 2
a am/pm marker PM
k Hour in day (1~24) 24
K Hour in am/pm (0~11) 0
z Time zone Pacific Standard Time
' Escape character for text 'at'
'' Display single quote '

Figures 7 and 8 show two properly formatted parameters and the results returned from the servlet.

Figure 7. Results for the format string yyyy.MM.dd+'at'+hh:mm:ss+zzz"
A screenshot of the proper format string for the parameter yyyy.MM.dd+'at'+hh:mm:ss+zzz

Figure 8. Results for the format string MMMM.dd.yyyy+'-'+hh:mm+aa"
A screenshot of the proper format string for the parameter MMMM.dd.yyyy+'-'+hh:mm+aa

The servlet is hosted at http://www.mycgiserver.com. This is a free hosting service based in Austria, so you shouldn't be surprised if the date format isn't familiar to your native language. For example, in Figure 7, the time zone is shown as CET, Central European Time, which we requested by specifying a time zone parameter of "zzz". However, if we change the time zone parameter to "zzzz" to get the full-text version of the time zone, the output may no longer be what you expect, as shown in Figure 9.

Eye on the clock
CET is used only during winter months in Austria. If you run the example shown in Figure 7 at another time of the year, the time zone may be CEST. See Resources to learn more about the CET and CEST time zones.

Figure 9. Results for the format string yyyy.MM.dd+'at'+hh:mm:ss+zzzz"
A screenshot of the proper format string for the parameteryyyy.MM.dd+'at'+hh:mm:ss+zzzz

Calling a servlet
The request to call a servlet begins inside the commandAction() method when the command cmRqst is invoked, as shown in Listing 10.

Listing 10. Event handling

/*--------------------------------------------------
* Call the servlet
*-------------------------------------------------*/
public void commandAction(Command c, Displayable s)
{
  if (c == cmRqst)
  {
    try
    {
      serverMsg = null;
      callServlet();
      if (serverMsg != null)
        fmMain.append(serverMsg);
    }
    catch (Exception e)
    {
      System.err.println("Msg: " + e.toString());
    }
  }
  else if (c == cmExit)
	{
	  destroyApp(false);
	  notifyDestroyed();
	}
}

The code for callServlet is shown in Listing 11. Notice the code for the client side consists only of setting the request method to GET. No header or body information is necessary for this MIDlet. You'll learn how to interpret the server response in just a moment from Listing 11 below.

Listing 11: Calling a servlet and processing the response

/*--------------------------------------------------
* Call the servlet
*-------------------------------------------------*/
private void callServlet() throws IOException
{
  HttpConnection http = null;
  InputStream iStrm = null;
  boolean ret = false;

  // Examples - Data is passed at the end of url for GET
  String url = "http://www.mycgiserver.com/servlet/corej2me.DateFormatServlet?
                format=MMMM.dd.yyyy+'-'+hh:mm+aa";
  // String url = "http://www.mycgiserver.com/servlet/corej2me.DateFormatServlet?
  //               format=yyyy.MM.dd+'at'+hh:mm:ss+zzz";

  try
  {
    http = (HttpConnection) Connector.open(url);

    //----------------
    // Client Request
    //----------------
    // 1) Send request method
    http.setRequestMethod(HttpConnection.GET);
    // 2) Send header information - none
    // 3) Send body/data -  data is at the end of URL

    //----------------
    // Server Response
    //----------------
    iStrm = http.openInputStream();
    // Three steps are processed in this method call
    ret = processServerResponse(http, iStrm);
  }
  finally
  {
    // Clean up
    if (iStrm != null)
      iStrm.close();
    if (http != null)
      http.close();
  }

  // Process request failed, show alert
  if (ret == false)
    showAlert(errorMsg);
}

The two lines of code that specify the URL show how to call our DateFormatServlet servlet from within the MIDlet. Simply looking at how the servlet is invoked gives us a hint that we will be using a GET request method, because all parameters are passed as part of the URL rather than being sent in a separate stream.

In Listing 12 you can see how we pass a parameter with the name format from the client to the server. The servlet will extract the value of this parameter to determine how to format the requested date.

Listing 12. Passing a format parameter

http://www.mycgiserver.com/servlet/corej2me.DateFormatServlet?format=
MMMM.dd.yyyy+'-'+hh:mm+aa;

http://www.mycgiserver.com/servlet/corej2me.DateFormatServlet?format=
yyyy.MM.dd+'at'+hh:mm:ss+zzz;

The servlet code
The servlet code is quite trivial. The bulk of what we are after is inside the method doPost(). We first store the format requested by the client in the variable format. Next, we call SimpleDateFormat to create an instance of our preferred date formatting object and follow this with a call to create a new Date object. We create a PrintWriter object as our output stream, set the content type to text/html and output the date in the requested format, as shown in Listing 13.

Listing 13. The servlet code

package corej2me;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.Locale;

public class DateFormatServlet extends HttpServlet
{
     public void init(ServletConfig config) throws ServletException
     {
       super.init(config);
     }

     public void doGet(HttpServletRequest request, HttpServletResponse response)
         throws ServletException, IOException
     {
       doPost(request, response);
     }

     public void doPost(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException
     {
       String format = request.getParameter("format");
       SimpleDateFormat simpleDate = new SimpleDateFormat(format);
       Date dt = new Date();

       PrintWriter out = response.getWriter();
       response.setContentType("text/html");
       out.println(simpleDate.format(dt));
       out.close();
     }

     public String getServletInfo()
     {
       return "DateFormatServlet";
     }
}

Parsing servlet results
The code for interpreting the server response is shown in Listing 14. The first check is to determine if the status line indicates the client request was successfully processed and a result returned (HTTP_OK). There is no header information, so we move directly to reading the server data from an input stream, storing the results in the variable serverMsg.

Listing 14. Parsing the servlet results

/*--------------------------------------------------
* Process a response from a server
*-------------------------------------------------*/
private boolean processServerResponse(HttpConnection http, InputStream iStrm) 
throws IOException
{
  //Reset error message
  errorMsg = null;

  // 1) Get status Line
  if (http.getResponseCode() == HttpConnection.HTTP_OK)
  {
    // 2) Get header information - none

    // 3) Get body (data)
    int length = (int) http.getLength();
    String str;
    if (length != -1)
    {
      byte servletData[] = new byte[length];
      iStrm.read(servletData);
      str = new String(servletData);
    }
    else  // Length not available...
    {
      ByteArrayOutputStream bStrm = new ByteArrayOutputStream();

      int ch;
      while ((ch = iStrm.read()) != -1)
        bStrm.write(ch);

      str = new String(bStrm.toByteArray());
      bStrm.close();
    }

   // Save the server message
   serverMsg = str;
   return true;

  }
  else
    // Use message from the servlet
    errorMsg = new String( http.getResponseMessage());

  return false;
}

For our next step we need to head back to the code inside commandAction(). After a successful call to callServlet() we append the saved string returned from the server onto the Form (our primary user interface component), which in turn displays the results on the device, as shown in Listing 15.

Listing 15. A closer look at commandAction()

/*--------------------------------------------------
* Call the servlet
*-------------------------------------------------*/
public void commandAction(Command c, Displayable s)
{
  if (c == cmRqst)
  {
    try
    {
      serverMsg = null;
      callServlet();
      if (serverMsg != null)
        fmMain.append(serverMsg);
    }

  ...

}

Again, here's the complete source code for the DateFormat MIDlet.

Conclusion
In this final article in the J2ME 101 series, you've had a chance to explore network communication support in MIDP. We began with an introduction to the Generic Connection Framework, the foundation for creating networked applications in MIDP. This was followed by a discussion of HTTP support, including how to package a client request, as well as interpret a server response. To demonstrate the concepts presented up to this point, we wrote a file-viewer MIDlet that downloaded and displayed a text file.

In the final section of this article we wrote a comprehensive MIDlet to access a Java servlet. This included a broad range of network communication operations, such as passing parameters to the servlet, interpreting and processing the parameters on the server side and, finally, retrieving and displaying the server response on the client.

In this four-part series, we've had a chance to take a close look at all the primary components of MIDP, namely, high-level and low-level user interface components, persistent storage, and network support. With the completion of this comprehensive overview, you now have a baseline of information to begin exploring and developing your own J2ME applications!

Resources

About the author

John Muchow, author of Core J2ME technology and MIDP, is a freelance technical writer and developer, with extensive experience in J2ME, Java technology, JSP, C, and ColdFusion. Visit Core J2ME for additional source code, articles, and developer resources. Send John an e-mail for additional information about writing or software development projects.



639 KBe-mail it!

What do you think of this document?
Killer! (5) Good stuff (4) So-so; not bad (3) Needs work (2) Lame! (1)

Comments?



developerWorks > Java technology | Wireless
developerWorks
  About IBM  |  Privacy  |  Terms of use  |  Contact