Although there are several examples and tutorials about how to connect to an HTTP server from a J2ME Midlet, I found that they all seem to concentrate a lot on the details of a GET and the POST is always assumed as an “exercise to the reader.”
Now, although it is indeed rather straightforward to work out how to send POST requests to a server, I found (the hard way) that there are certain “minor details” that are best not forgotten.
I have thus decided to post here a brief example of how to send a request to a server, using a form-encoded body, so that either a JSP or a servlet can retriever the parameters, but one can benefit from the many advantages of POSTing, as oppposed to GETting.
The source code follows, I believe it is reasonably self-explanatory and comments should guide even the most novice reader through, but please do let me know if you feel that this is still too cryptic:
private void OpenConnection(String server)
throws java.io.IOException {
// TODO: the actual "resource" part of the URL (/test in this case)
// should be either passed as a parameter or obtained at application
// level (maybe even set up at runtime).
// To test this code, I have written the simple Servlet below, however
// this would also work with a JSP page that echoes back the contents
// in text/plain format for the midlet to display.
//
String url = "http://"+server+"/test";
byte[] data = null;
InputStream istrm = null;
HttpConnection http = (HttpConnection)Connector.open(url);
http.setRequestMethod(HttpConnection.POST);
// This allows a JSP page to process the parameters correctly
// This format (or content type) is the same as when forms are
// submitted from a web page.
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// You may want to indicate to the server side (eg servlet) that
// this request is coming from a specific application. This would
// allow generation of appropriate format for the content.
http.setRequestProperty("User-Agent", "HttpMidlet/0.2");
// You can send any custom property as part of the HTTP header
// This would matter for the "infrastructure" code, as opposed
// to "body parameters" that would matter to the application
// (eg, user=marco)
http.setRequestProperty("Custom-Property",
"MyCustomProperty/1.0; AnotherProperty/debug_0.1");
// You MUST create the body of the post BEFORE actually opening
// the stream, otherwise you won't be able to set the length of
// the request (see next step).
// In this example, I am sending coordinates to a mapping server
String msg = "x="+location.getX()+"&y="+location.getY();
// THIS is important! without it a JSP won't process the POST data
// it would also appear that CASE MATTERS (using "Content-Length" -note
// the capital 'L'- caused my servlet to return -1 from the
// HttpServletRequest.getContentLenght() method
http.setRequestProperty("Content-length", ""+msg.getBytes().length);
// After this point, any call to http.setRequestProperty() will
// cause an IOException
OutputStream out = http.openOutputStream();
out.write(msg.getBytes());
out.flush();
if (http.getResponseCode() == HttpConnection.HTTP_OK) {
int len = (int)http.getLength();
istrm = http.openInputStream();
if (istrm == null) {
log("Cannot open stream - aborting");
throw new IOException("Cannot open HTTP InputStream, aborting");
}
if (len != -1) {
data = new byte[len];
int bytesRead = istrm.read(data);
addProgressMsg("Read "+bytesRead+" bytes");
} else {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
int ch;
int count = 0;
// This is obviously not particularly efficient
// You may want to use a byte[] buffer to read bytes in chunks
while ((ch = istrm.read()) != -1) {
bo.write(ch);
count++;
}
data = bo.toByteArray();
bo.close();
addProgressMsg("Read "+count+" bytes");
}
response = new String(data);
addProgressMsg("finished");
} else {
log("Response: "+http.getResponseCode()+", "+http.getResponseMessage());
response = null;
addProgressMsg("failed: "+http.getResponseMessage());
}
// This is critical, unless you close the HTTP connection, the application
// will either be consuming needlessly resources or, even worse, sending
// 'keep-alive' data, causing your user to foot unwanted bills!
http.close();
}
To test this code, all that is required is a simple Serlvet that runs, for example, in an Apache Tomcat JSP container:
public class ConnectivityDiag extends HttpServlet {
private static final String REVISION = "0.0.04";
private static Logger log;
public ConnectivityDiag() {
if (log == null) {
log = Logger.getLogger(getClass().getName());
BasicConfigurator.configure();
}
}
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doProcessRequest(req, res);
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doProcessRequest(req, res);
}
public void doProcessRequest(HttpServletRequest req, HttpServletResponse res) {
BufferedReader reader = null;
Writer writer = null;
try {
// Note this call seems to be case-sensitive
// if the midlet does not set exactly "Content-length"
// as the property, -1 will be returned
int len = req.getContentLength();
String contentType = req.getContentType();
log.debug("Len: " + len);
log.debug("Type: "+contentType);
int bytesRead = 0;
String remoteHost = req.getRemoteHost();
String userAgent = req.getHeader("user-agent");
log.info("Accessed at " + (new Date()) + " from " + remoteHost
+ ", using " + userAgent);
// This simply echoes the parameters back to the client.
// A JSP would use these in a tag
// and the ${param} macro.
ArrayList strings = new ArrayList();
Enumeration e = req.getParameterNames();
while (e.hasMoreElements()) {
String param = (String)e.nextElement();
String value = (String)req.getParameter(param);
strings.add(param+" = "+value);
}
res.setContentType("text/plain");
// This is a custom property, that the remote client
// could query
res.setHeader("Sent-at", new Date().toString());
writer = res.getWriter();
writer.write("Diagnostic servlet - Rev. "+REVISION);
writer.write("\nBytes received: " + len);
writer.write("\nFrom: " + remoteHost);
writer.write("\nUsing: " + userAgent);
writer.write("\n=== Parameters ===\n");
for(String s:strings) {
writer.write(s);
writer.write('\n');
}
writer.write("=== Parameters end here ===\n");
writer.flush();
} catch (IOException e) {
log.error(e.getMessage());
} finally {
try {
if (reader != null)
reader.close();
if (writer != null)
writer.close();
} catch (IOException ex) {
log.error(ex.getMessage());
}
}
}
}
Leave a Reply