6.2 The Process-Chain Approach In this section, I present a process-chain-based HTTP transport framework that works on all J2ME platforms. For simplicity, the new transport class treats all requests and responses as byte arrays rather than streams. If your application requires stream I/O (e.g., a SAX XML parser), you can easily wrap a ByteArrayInputStream or ByteArrayOutputStream around those arrays. The key components in the new framework are listed in Table 6.1.Table 6.1. The HttpClient Framework|
HttpClient | It is the main class. Developers first set the connection URL and HTTP request method using setUrl() and setRequestMethod() methods. Then, the request byte array is passed to method query(). The return value of query() is the response byte array. | Handler | Class HttpClient can have a chain of HTTP header handlers. Each handler implements the Handler interface. The Handler interface declares only two methods. The prepareHeaders() method sets headers for the request; the processHeaders() method processes headers from the response. The processHeaders() method returns a boolean value indicating whether the HttpClient object needs to resubmit its request after this round of header processing. |
6.2.1 The HttpClient Source Code The source code of the HttpClient class is shown in Listing 6.3. Notice how we walk through the handlers chain twice to process both the request and response headers in the query() method. The maxIteration property is used to prevent infinite loops in case of failed challenge-response cycles.Listing 6.3. The HttpClient class
public class HttpClient { private String url; private String requestMethod; private Vector handlers = new Vector (); // Max number of challenge/response cycles. private int maxIteration = 3; public HttpClient() {} public void setUrl (String url) { this.url = url; } public void setRequestMethod (String method) { this.requestMethod = method; } public void setMaxIteration (int n) { maxIteration = n; } public void addHandler (Handler h) throws Exception { handlers.addElement(h); } public void removeAllHandlers () throws Exception { handlers = new Vector (); } public byte [] query (byte [] req) throws Exception { boolean needConnect = true; HttpConnection c = null; int currentIteration = 0; while (needConnect) { currentIteration++; if (currentIteration > maxIteration) throw new Exception("Too many Iterations"); needConnect = false; if ( c != null ) { try { c.close(); } catch (Exception ignore) { } } c = (HttpConnection) Connector.open (url); c.setRequestMethod( requestMethod ); for (int i = 0; i < handlers.size(); i++) ((Handler) handlers.elementAt(i)).prepareHeaders(c); c.setRequestProperty("User-Agent", "Profile/MIDP-1.0, Configuration/CLDC-1.0"); c.setRequestProperty("Content-Language", "en-US"); if ( req != null ) { OutputStream os = c.openOutputStream (); os.write(req); os.close(); } for (int i = 0; i < handlers.size(); i++) { needConnect = ((Handler) handlers.elementAt(i)).processHeaders(c) | | needConnect; } } InputStream is = c.openInputStream (); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buf = new byte[256]; while (true) { int rd = is.read(buf, 0, 256); if (rd == -1) break; bos.write(buf, 0, rd); } buf = bos.toByteArray(); is.close(); c.close(); return buf; } }
Now, let's look at how to use those two frameworks to handle HTTP headers in the real world. |