|
|
Contents: |
|
|
|
Related content: |
|
|
|
Subscriptions: |
|
|
| Network support in J2ME/MIDP
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
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.
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
Once the image is received it is displayed on the device as shown in
Figure 3.
Figure 3. 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
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
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
Figure 6 shows the fields returned from the server, with the values
written to the console.
Figure 6. 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"
Figure 8. Results for the format string
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"
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
- Download the J2ME Wireless Toolkit
version 2.0.
- Don't miss the other content in the J2ME 101 series:
- Part
1 provides an introduction to the MIDP high-level interface.
- Part
2 details MIDP s low-level interface.
- Part
3 goes inside the Record Management System.
- If you are new to the Wireless Toolkit, "MIDlet development with
the Wireless Toolkit" (developerWorks, March
2003) provides an excellent starting point for learning to use
it.
- "Extend J2ME to Wireless
Messaging" (developerWorks,
February 2003) explores Short Message Service (SMS) and Cell Broadcast
Service (CBS) message support in J2ME.
- Learn more about getting Java applications to network on small
devices, with Soma Ghosh's "Networking with J2ME"
(developerWorks,
September 2002).
- Keep on learning about J2ME, with the latest tutorial, "Work with sprites in
J2ME" (developerWorks,
December 2003), where you'll learn how to spice up your applications
with more complex, malleable images.
- The KVM mailing list is an
excellent resource for J2ME developers, offering a searchable archive
and an active email discussion area.
- Sun Microsystems CLDC and MIDP wireless
forum lets you post questions and search for J2ME-related
information.
- Core J2ME (Prentice
Hall PTR, 2002) by John W. Muchow is a comprehensive guide to J2ME
development. You can also visit the Core J2ME Web site for
additional articles, tutorials and developer resources.
- The WebSphere Micro
Environment provides an end-to-end solution connecting cellular
phones, PDAs, and other pervasive devices to e-business.
- The alphaWorks Web Services Toolkit for
Mobile Devices provides tools and a runtime environments for
developing applications that use Web services on small mobile devices,
gateway devices, and intelligent controllers.
- Visit http://www.timeanddate.com/library/abbreviations/timezones/eu/cet.html
for additional information about the CET and CEST time zones.
- The official PNG Web site will get
you started with learning about the Portable Network Graphics image
format.
- The developerWorks
Wireless zone offers a wealth of technical content on pervasive
computing.
- You'll find hundreds of articles about every aspect of Java
programming in the developerWorks Java
technology zone.
- Also see the Java technology zone
tutorials page for a complete listing of free tutorials from developerWorks.
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. |
|