J2ME Guide – Part 7

Until now we’ve learned how to create GUIs for our device applications using Low and High level APIs, which are at the base of every MIDlet application. Another fundamental feature we need to learn is the way our device communicates to the world using some kind of connection. For instance, we can think about an application connecting to a server in order to send or to receive emails; another – more sophisticated – example may be an application which collects GPS information data and then sends it to a server which plots the sender’s position on a map. The capability to connect to the external world is a fundamental feature in the MIDP 2.0 specifications. As we’ll see later, we can think about two different kinds of APIs for managing I/O connections that we can classify in:

Subset of the J2SE API
Specific MIDP Profile and CLDC configuration APIs

As we learned in the first module (fig. 1), MIDP is based on CLDC, a subset of the J2SE API plus some small other APIs. We only can employ a subset of the full J2SE API because the power and the capabilities of a desktop application are different from the ones of an application running on a device with limited CPU and memory resources. For instance, we know that MIDP is based on CLDC, but it doesn’t require a specific version of the CLDC itself. So, if the device supports the CLDC 1.1 version we can use floating point; otherwise – if the supported version is the 1.0 – we can’t. In the next sections we’ll have a look at these APIs.
The second part of our module is about the MIDP/CLDC specific part of the I/O API, called Generic Connection Framework (GCF). The main feature of the GCF is that it must be open and must make available several kinds of protocols to the device. As we’ll see later in details, the GCF is a kind of Factory Pattern implementation, which defines the Connector class that gives us a specific Connection interface realization from a specific URL. In this way, it’s the URL which defines the specific Connection implementation and not a specific class. The device may be able to support a specific protocol or not, and we can manage this is through a simple exception management mechanism.

We’ll finish this module with a simple MIDlet which connects to a specific URL showing the result on the screen. We can then use this MIDlet to test whether a device manages or not a specific protocol. However, we have to remember that MIDP 2.0 specifications tell us that HTTP and HTTPS protocols are a MUST for any device.

Fig. 7.1
fig. 1

The java.io package

The java.io package defined into the MIDP/CLDC profile is a subset of the J2SE’s package bearing the same name. When we talk about a subset we don’t only say that we have now less classes, but also that some of the remaining classes have less methods. That’s clearly a consequence of the relationships between classes.
The main interfaces and classes belonging to this package are shown in fig. 2 and fig. 3 (covering input and output operations respectively). In the first diagram we can see the InputStream abstract class which is the most important one. This class describes methods about reading bytes. With an InputStream implementation we can read a single byte or fill a byte array with data from some data source. As we saw in the fifth module about object serialization, sometimes we need to read not just bytes but the values of some fundamental types such as int or the instances of some special classes such as the String class. To do that we can use the DataInputStream which is an InputStream wrapper implementation that reads a number of bytes accordingly to the type of the value to read, hiding the byte-level structure of the value itself. The DataInputStream has a set of utility methods described by the DataInput interface.

fig. 2
fig. 2

fig. 3
fig. 3

In the same diagram we can see the class ByteArrayInputStream which is another very useful InputStream implementation. In the fifth module we saw how to employ this class to serialize objects so that we could insert them into a RMS repository. In fig. 3 we have the corresponding output classes and interfaces.
We saw classes and interfaces designed to manage I/O operations on bytes. The java.io package has also some other classes and interfaces useful to manage I/O operations with chars. In fig. 4 we can see the Reader and Writer abstract classes with their realizations. In this figure we can see how the InputStreamReader is a Reader which implements operations with chars delegating those operations to an InputStream it wraps. The same applies for the Writer and OutputStreamWriter.

As we can see from the JavaDoc documentation, when we develop a MIDlet we can use a small set of classes which nevertheless gives us all the tools we need to manage information.

fig. 4
fig. 4

The Generic Connection Framework

In the previous paragraph we saw that the java.io package is a subset of the J2SE java.io package. As we said, some other classes are inherited from the CLDC configuration and are collected into the javax.microedition.io package. We’re talking about the GCF; the main concept is described in fig. 5.

fig. 5
fig. 5

As we can see from the javadoc, we have just two classes: the Connector class, which is the core of the GCF, and the PushRegistry class that we’ll study in details in a future module. The Connector class is the class we’ll use to create every type of connection through its open method, which is static and has three different overloadings:

public static Connection Connector.open(String uri)

public static Connection Connector.open(String uri, int mode)

public static Connection Connector.open(String uri, int mode, boolean timeouts)


each returning a Connection reference type. The URI is an identifier of the type of Connection we need, containing also specific information about that. The syntax of the URI value is:

protocol:address;parameter


where protocol is the fundamental part and decides the specific Connection implementation the Connector will return. The address is the location identifier of the resource. The parameter is a set of name=value pairs we can insert, divided by the “;” (semicolon) char Some examples of URIs are:

http://mobile.actionscript.com:8080

for a HTTP Connection or:

socket://mobile.actionscript.com:8000

for a socket connection. In some cases we need to specify some additional information as is the case with a serial connection

comm:0;baud=9600


The mode is a parameter whose allowable values are:

Connector.READ
Connector.READ_WRITE
Connector.WRITE

The timeout parameter specifies a timeout value, employed in the creation of a new Connection.
Since the Connector class is a Factory of Connection implementations, we can use it in this way:

Connection connection = null;
try{
connection = Connector.open("protocol:address;parameter");
}catch(ConnectionNotFoundException cnfe){
// There isn't any Connection implementation for the specified URI
} catch(IOException ioe){
// Error while creating the Connection
} catch(IllegalArgumentExceptio iae){
// Wrong connection creation mode
}


The kinds of Connection we can get from a Connector are depicted in fig. 6.

fig. 6
fig. 6

As we can see from the diagram we have a set of interfaces that the CLDC device implementation can or must implement. Every MIDP 2.0 compliant device may or may not implement the SocketConnection, but must support the HttpConnection. We’ll learn about these implementations in the next section. We’ll now talk a little about the other methods of the Connector class, which allow us to obtain an Input/Output stream from a Connection or from a DataInput/OutputStream. They’re useful for getting InputStreams and OutputStreams directly from the Connector without passing through a Connection reference. We’re talking about these methods:

public static InputStream Connector.openInputStream(String uri)
public static OutputStream Connector.openOutputStream(String uri)


and

public static DataInputStream Connector.openDataInputStream(String uri)
public static DataOutputStream Connector.openDataOutputStream(String uri)


As we said before, a device may or may not manage a specific connection. Some types of connection, such as the HTTP and HTTPS, must be managed by a MIDP 2.0 compliant device. Some other kinds of connection, such as the SocketConnection, may not be managed. If we try to open a not supported type of connection, the open method throws a ConnectionNotFoundException we must catch.

Different Connection Implementation

In fig. 6 we can see all the Connection specializations we’ll describe in the present section. As we said before, they are interfaces devices can implement in a native way for better performances. We start from the Connection interface that, as we can see from the javadoc, describes just the following method:

public void close() throws IOException


that tells us that every Connection can be closed. This is not enough for us but this is the only method that every Connection must have. If we want to manage streams we have to get a Connection; that’s to say an InputConnection and/or an OutputConnection, two Connection specializations describing methods to get streams:

public InputStream openInputStream() throws IOException
public DataInputStream openDataInputStream() throws IOException


for the InputConnection and

public OutputStream openOutputStream() throws IOException
public DataOutputStream openDataOutputStream() throws IOException


for the OutputConnection. These are exactly the methods we saw before when talking about the Connector class. So, the StreamConnection is the Connection which can manage both an InputStream and an OutputStream. We can notice here the multiple inheritance, which in Java doesn’t exist for classes but exists for interfaces as in this case.

We continue our path through the Connection hierarchy with the ContentConnection which is a StreamConnection with additional information about the type, the encoding and the length of the information we want to manage. The ContentConnection provides us these information through these methods:

public String getType()

public String getEncoding()

public long getLength()


The most important kind of ContentConnection is the HttpConnection which describes a set of methods about the HTTP protocol management. In this case it’s very interesting the meaning of the previous methods. The getType() method returns the Content-Type value of the HTTP header, the getEncoding() method returns the Content-Encoding value of the HTTP header and the getLength() method returns the Content-Length value of the HTTP header. As we’ll see in the next section, the HttpConnection is the most important Connection because every device must implement it. A more specific implementation is the the HttpsConnection which manages a secure Connection based on HTTPS.

Another kind of ContentConnections are the SocketConnection for socket management and the CommConnection, very useful for serial connections with devices that support, for instance, Irda.

At the end, we’ll say a few words about the StreamSocketNotifier and the DatagramConnection. The former allows our device to simulate a server for socket communications. The latter allows our device to manage datagram connections for streaming and things like that.

Managing HttpConnections

Before developing our example application we want to describe some of the features of the most important Connection, a kind of connection every device must implement: the HttpConnection. As we saw before, the HttpConnection is a ContentConnection and so it manages some types of contents with a given encoding. If we want to use a request/response paradigm this is the right Connection type. To communicate with this kind of Connection we must follow the following three steps:

- setup
- connection
- closing

In the setup step we can set all the information about the connection. We’re talking about header values, parameters, request methods and things like that. In this case we don’t create the connection in the connection step; instead, the device starts the connection with the information set in the setup phase. After the request is sent and the response is obtained, the Connection can be closed. These three steps are important because some of the HttpConnection methods can be called in just one of them and because some other methods allows the transition from a step to the next.

When we are in the setup phase we can set the request method in this way:

public void setRequestMethod(String method)throws IOException


where the parameter can be one of these values:

HttpConnection.GET
HttpConnection.POST
HttpConnection.HEAD


other values are not useful for connection from a small device.

Another kind of information we can insert into a request regards headers and parameters. To do that we can use this method:

public void setRequestProperty(String key, String value)throws IOException


Some examples of this are:

...
connection.setRequestProperty("User-Agent","Profile/MIDP2.0 Configuration/CLDC-1.0");
connection.setRequestProperty("Content-Language","en-US");
connection.setRequestProperty("Accept","text/xml,text/html");
connection.setRequestProperty("Content-type","application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length",buffer.length());
...


for headers and

connection.setRequestProperty("key","value_1");
connection.setRequestProperty("key","value_2");


for parameters.

After the setup phase we can start the connection. We already know how to do that; that’s to say, we have to call one of the following methods:

public InputStream openInputStream() throws IOException
public DataInputStream openDataInputStream() throws IOException

or a method about the content as described by the ContentConnection.

Very important are the following methods, whose goal is to start the connection:

public int getResponseCode() throws IOException
public String getResponseMessage() throws IOException


The first is a fundamental method that gives us the HTTP response code we can check using one of the constants the HttpConnection interface describes. The most important of them are:

HttpConnection.HTTP_OK
HttpConnection.HTTP_NOT_FOUND
HttpConnection.HTTP_FORBIDDEN

Other useful methods to get header information are:

public String getHeaderField(String name) throws IOException
public int getHeaderFieldInt(String name, int def) throws IOException
public long getHeaderFieldDate(String name, long def) throws IOException
public String getHeaderField(int n)throws IOException


Clearly, to close a connection we just call the method close() that every Connection has.
As an example of the HttpConnection we want to describe how to make a connection with a POST method. The code that does that is:

public void postViaHttpConnection(String url) throws IOException {
// Create HttpConnection Reference
HttpConnection c = null;
// Create an InputStream reference for reading
InputStream is = null;
// Create an OutputStream reference for writing
OutputStream os = null;
// Using for response code
int responseCode;
try {
// Initializes the HttpConnection. It's not open yet
// We're in the setup state
c = (HttpConnection)Connector.open(url);
// Sets the request method
c.setRequestMethod(HttpConnection.POST);
// Sets some headers for example
c.setRequestProperty("If-Modified-Since","29 Oct 1999 19:43:31 GMT");
c.setRequestProperty("User-Agent","Profile/MIDP-2.0 Configuration/CLDC-1.0");
c.setRequestProperty("Content-Language", "en-US");
// Here we want to set post data so we get OutputStream
os = c.openOutputStream();
// We have to write as byte array the data set in POST
os.write("LIST gamesn".getBytes());
// It's not mandatory but we flush
os.flush();
// Now we want to get data and so we want to start the connection. To do that
// we invoke one of the methods that start the connection. In this case we
// get the response code
responseCode = c.getResponseCode();
// Check for error in the response code
if ( responseCode != HttpConnection.HTTP_OK) {
throw new IOException("HTTP response code: " + responseCode);
}
// If it's ok we start to read the response from the InputStream
is = c.openInputStream();
// The HttpConnection is a ContentConnection so we get the Content Type
// we can manage later for some use
String type = c.getType();
// We ask the connection the data length
int len = (int)c.getLength();
// Some implementations don't give us this information so we have to
// manage both cases.
if (len > 0) {
// In this case the length information is available so we use the buffered version
int actual = 0;
int bytesread = 0 ;
// Buffer
byte[] data = new byte[len];
while ((bytesread != len) && (actual != -1)) {
actual = is.read(data, bytesread, len - bytesread);
bytesread += actual;
}
// Here we can do something with data
process(data);
} else {
// In this case we don't have the length information so we read
// until the end of data
int ch;
while ((ch = is.read()) != -1) {
process((byte)ch);
}
}
} catch (ClassCastException e) {
throw new IllegalArgumentException("Not an HTTP URL");
} finally {
// Closing all open things
if (is != null)
is.close();
if (os != null)
os.close();
if (c != null)
c.close();
}
}


As we can see in the code, in order to create an HttpConnection with a POST method we followed three steps. We created the HttpConnection, and we set the request methods, headers and parameters. We started the connection and read the result. At the end we closed everything. A last important thing we’ll see later is that this operations must be executed in a different thread from the main application’s thread. This will avoid the device to fall into a dead lock.

Example: A MIDlet to manage Connection

As a final example, we want to create a simple MIDlet that connects to a given URI returning some content. To do that we’ll create the MIDlet ConnectionTestMidle that we can easily understand since it uses only high level apis we studied in the fourth module. The aim of our application now is the connection management which is described by the class ConnectionThread. As we said before, it’s fundamental that the connection is executed inside a different thread from the main one. That’s because we don’t want our application to be stuck if the connection fails or something else goes wrong. This can be done easily since the class ConnectionThread is a Thread. All the important code of the thread is described inside the run() method. Here we do some simple operations. First we use the Connector to create a Connection with the given URI. The ConnectionThreadListener interface is used for connection data or error notification. After that, from the Connection we get the InputStream for data reading. We use a ByteArrayOutputStream to collect data because it allows us to use some useful methods for bytes management.

We know that if the Connection is a ContentConnection we can get more information about type, encoding and length. What we did is checking the Connection type to get that information. We did just the same for the HttpConnection interface, getting some useful information such as the HTTP response code. To try this MIDlet we can execute it and insert some http url (fig. 7). What we get is the content of the page requested (fig. 8).

.fig. 7
fig. 7

fig. 8
fig. 8

Conclusion

In this module we covered many important concepts about midlet I/O and networking. We learned about the main J2SE I/O API, contained into the java.io package and the API inherited from the CLDC implementation contained into the javax.microedition.io package, called Generic Connection Framework. Then, we saw how to create an HttpConnection getting specific information. We created a simple MIDlet employing that API to read data from a given URI.

References

[1] MIDP 2.0 API http://java.sun.com/javame/reference/apis/jsr118/
[2] API MIDP 2.0 documentation

Related Download: work.zip

Comments

comments

Leave a Reply

Your email address will not be published.


*