The Generic Connection Framework

The Generic Connection Framework is the standard means of performing I/O, especially network I/O, in all profiles of J2ME. It is available in the CLDC, the MIDP, the Information Module Profile (IMP), and all CDC-derived profiles, including the Foundation Profile, the Personal Basis Profile, and the Personal Profile. As a result, Im not going to worry a lot about exactly which profile you e using. For our purposes, they e equivalent.

Whats not equivalent are the kinds of I/O the different devices support. Most embedded devices don have filesystems, but some of the larger ones, such as the iPod, do. Some devices have no network access at all; some have full, unrestricted IP stacks; and some have access to some sort of proprietary network. Devices can have serial ports, parallel ports, USB ports, FireWire ports, and/or Bluetooth capabilities. Much like streams, the GCF is designed to allow particular devices to support all the different forms of I/O the devices have, but none of the ones they don . This rules out classes like URL, Socket, FileInputStream, and UsbDevice that are closely tied to one particular kind of I/O service. No one wants to waste precious space on a FileInputStream class for a device that doesn have a filesystem.

The GCF is based on an abstract Connection interface, which supports two basic kinds of connections: packet and stream. Packet connections are used for UDP and Bluetooth L2CAP. Stream connections are used for TCP, files, serial ports, and Bluetooth RFCOMM. Streams are read and written using the regular InputStream and OutputStream classes.

Reading input using the GCF follows these three steps:

  1. Pass the URL of the resource you want to read to the static Connector.openInputStream( ) method. This returns an InputStream object.

  2. Read from the InputStream in the usual way.

  3. Close the InputStream when you e done.

For example, this code fragment opens a connection to Google:

InputStream in = Connector.openInputStream("http://www.google.com/"); // read from in like you would any other InputStream... in.close( );

Output is similar, except you open an OutputStream instead of an InputStream:

OutputStream out = Connector.openOutputStream("socket://ftp.example.com:8979"); // write to out as you would any other OutputStream... out.close( );

So far this looks a lot like the URL class, but with three key differences:

For example, if you wanted to open a socket to Google to read the headers of the page instead of just the body, you would use the nonstandard socket protocol, like this:

InputStream in = Connector.openInputStream("socket://www.google.com:80/"); // read from in like you would any other InputStream... in.close( );

As in applets, access to the network is restricted to signed, trusted MIDlets. Untrusted MIDlets prompt the user for authorization before each potentially dangerous operation.

Example 24-1 shows a simple MIDlet program that displays the current time. It does this by connecting to the National Institute of Standards time server in Boulder, Colorado on the daytime port (13) and displaying the result. The daytime protocol sends a one-line ASCII string that is easy to display on most small MIDP 1.0 devices.

Example 24-1. A MIDlet daytime client

import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.io.*; import java.io.*; public class Daytime extends MIDlet { public Daytime( ) { Form form = new Form("Network Time"); InputStream in = null; try { in = Connector.openInputStream("socket://time-a.nist.gov:13"); StringBuffer sb = new StringBuffer( ); for (int c = in.read(); c != -1; c = in.read( )) { sb.append((char) c); } form.append(sb.toString( )); } catch (IOException ex) { form.append(ex.getMessage( )); } finally { try { if (in != null) in.close( ); } catch (IOException ex) { /* Oh well. We tried.*/ } } Display.getDisplay(this).setCurrent(form); } public void startApp( ) {} public void pauseApp( ) {} public void destroyApp(boolean unconditional) {} }

Figure 24-1 shows this program displaying in the J2ME emulator bundled with the Sun Java Wireless Toolkit. When running this MIDlet, the phone prompts the user to allow the network connection. These prompts can be eliminated, but normally only if you can cut a deal with the phone company. Cell phones tend to be locked-down devices that do not allow arbitrary hacking.

Figure 24-1. The daytime MIDlet

Note the lack of a main( ) method. A MIDlet is conceptually more like an applet than an application. It cannot be compiled and run like a typical standalone Java application. Im assuming here you know how to compile and run MIDlets. If not, you can consult any number of good books on the topic, including J2ME in a Nutshell by Kim Topley (OReilly).

The socket protocol used here is available on some, but not all, MIDP devices. It is not turned on in the emulator by default. To enable it, set the Java system property com.sun.midp.io.enable_extra_protocols to TRue. You can do this with the -D command-line option or by editing the $(MIDP_HOME)/lib/internal.config file, where the emulator reads its configuration information.

.1.1. The Connector Class

The Connector class contains all the static utility methods needed to open connections, regardless of what kinds of connections they are. Youve already seen the openInputStream( ) and openOutputStream( ) methods:

public static InputStream openInputStream(String url) throws ConnectionNotFoundException, IOException, IllegalArgumentException public static OutputStream openOutputStream(String url) throws ConnectionNotFoundException, IOException, IllegalArgumentException

These methods throw an IllegalArgumentException if the URL is malformed, a ConnectionNotFoundException (a subclass of IOException) if the remote is unreachable or the protocol is not supported, and an IOException if the stream can be opened for any other reason.

Other methods open DataInputStreams and DataOutputStreams instead:

public static DataInputStream openDataInputStream(String url) throws ConnectionNotFoundException, IOException, IllegalArgumentException public static DataOutputStream openDataOutputStream(String url) throws ConnectionNotFoundException, IOException, IllegalArgumentException

However, these four are the only kinds of streams you have. There are no object streams, piped streams, cipher streams, or any other kinds of filter streams in J2ME. Indeed, theres no FilterInputStream or FilterOutputStream class at all. In J2ME, DataInputStream and DataOutputStream extend InputStream and OutputStream directly:

public class DataInputStream extends InputStream implements DataInput public class DataOutputStream extends OutputStream implements DataOutput

Another difference worth noting: the DataInputStream class has removed the deprecated readLine( ) method. In CLDC 1.0, readFloat( ) and readDouble( ) are also omitted, though these are present in CLDC 1.1 and CDC devices.

Instead of requesting a stream directly, you can ask the connector for a Connection object using one of its three open( ) methods:

public static Connection open(String url) throws IOException public static Connection open(String url, int mode) throws IOException public static Connection open(String url, int mode, boolean timeouts) throws IOException

There are two kinds of Connections: InputConnections and OutputConnections. The InputConnection interface has openInputStream( ) and openDataInputStream( ) methods. The OutputConnection interface has openOutputStream( ) and openDataOutputStream( ) methods. Once you have a connection, youll have to cast it to one of these types to use it. For example, this code fragment opens a connection to Google and reads from it:

Connection conn = Connector.open("http://www.google.com/"); InputConnection input = (InputConnection) c; InputStream in = input.openInputStream( ); // read from in like you would any other InputStream... conn.close( );

Some, though not all, objects implement both InputConnection and OutputConnection. Some objects implement only one or the other. If you know which you want, pass the appropriate constant (Connector.READ, Connector.WRITE, or Connector.READ_WRITE) to the open( ) method. For example, this line opens a read-only connection to Google:

Connection c = Connector.open("http://www.google.com/", Connector.READ);

If a mode the protocol does not support is passed, open( ) throws an IllegalArgumentException.

Your final option is to pass true for the third argument. This indicates that the program is ready to handle timeouts and that the connection should throw a java.io.InterruptedIOException if the connection times out. (In practice, this doesn have a lot of effect. While theoretically a connection might hang forever without this, in practice some exception is likely to be thrown sometime. It just won be an InterruptedIOException.)

None of this is very different from just calling openInputStream( ) or openOutputStream( ) and working with that stream. What, then, is the point of open( )? If you know what kind of connection you e opening (file, HTTP, socket, etc.)and you normally do know thisyou can cast the returned InputConnection or OutputConnection to a more specific type: ContentConnection , HttpConnection, SocketConnection, and so on. Then you can use the extra methods of these classes to do interesting things.

Категории

© amp.flylib.com,