Sunday, June 12, 2011

First Android app published

Yesterday I published my first Android app on Android market, UnSilence. The background is that I tend to forget to turn my cellphone back to normal after I've set it to silent during a meeting. I have missed too many calls beacuse of that...

So UnSilence helps you be allowing you to set your phone to silent (with or without vibaration) for a period of time, and then automatically turn sound back on.

The main design goal was too keep it simple, one click to start the app, and one click to set to silent, that's all.

Friday, March 18, 2011

Servlet http proxy

For a while I have been working on a project where we are trying to integrate a windows-based service in a Java EE environment. Basically the problem is to host an Ajax application under an application server like IBM Webspehere or Apache Tomcat. We also want to support portlets following the JSR 268 specifikation.

I googled around a bit and found lots of useful stuff, but nothing that did all that we wanted. The mainly problems was with headers and cookies, since we need to keep a session to the servicein the background we need to make those work. So I ended up writing my own. Not much code, but it might be useful for someone else, as it is pretty general stuff.

The service method

Since we want to handle all HTTP calls, we override the service method of HttpServlet. If you want only some methods (GET and PUT for example) to work you might just override doGet and doPost for example, but we want it all.. The implementation looks like this:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String qry = req.getQueryString();
URL url = new URL(protocol, host, port, req.getRequestURI()
+ (qry == null ? "" : "?" + qry));
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setUseCaches(false);
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setRequestMethod(req.getMethod());
handleRequestHeaders(req, urlConnection);
handlePostData(req, urlConnection);
// make the call - get response code
int responseCode = urlConnection.getResponseCode();

resp.setStatus(responseCode);
if (responseCode != HttpURLConnection.HTTP_OK)
log.info("HTTP code:" + responseCode + " "
+ urlConnection.getResponseMessage() + " URL["
+ url.toString() + "] " + req.getMethod());
else
log.debug("HTTP code:" + responseCode + " URL[" + url.toString()
+ "] " + req.getMethod());
handleResponseHeaders(resp, urlConnection);
// send output to client
handleContent(resp, urlConnection, responseCode);
}

This is pretty straight forward. The protocol (http/https), server and port to use are configurable. We take the URI from the request and add the query string, if there is one. Together with the configure host etc, this forms the new URL, which we open, using the same method as in the call. We set some parameters, request headers and post data if there is any.

We then call the URL, check the HTTP response code, and take care of the headers and the response, which all are sent to the client.

Handle headers
The part that we really needed was the headers. This simply transfers the headers between the two connections, with a few exceptions:

private void handleRequestHeaders(HttpServletRequest req,
HttpURLConnection urlConnection) {
// set request headers
Enumeration e = req.getHeaderNames();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
if (!"Host".equalsIgnoreCase(key)
&& !"Accept-Encoding".equalsIgnoreCase(key)) {
String value = req.getHeader(key);
log.debug("Request Header: " + key + ": " + value);
urlConnection.setRequestProperty((String) key, value);
}
}
//add your own headers here
}

There are two headers that we dont want to transfer from the client's request to our new request:
  1. The Host header, since this will be our proxy server. The new Host header will be added automatically, so we don't need to think about it.
  2. The Accept-Encoding header. This is a fix, the Accept-Encoding header turns gzip on, and since we have not implemented gzip in our proxy we don't want it. Another alternative would be to turn gzipping of requests off in our web server, perhaps this would have been better. If we do transfer th Accept-Encoding header, both servers might zip the content, which will not work.
Finally in this method we add our own headers, which is basically the reason why we need the proxy at all.

When we get the response back from the server we have the corresponding method to handle the response headers:

private void handleResponseHeaders(HttpServletResponse resp,
HttpURLConnection urlConnection) {
// set response headers
Map<string,>> headers = urlConnection.getHeaderFields();
Set<map.entry<string,>>> entrySet = headers.entrySet();
for (Map.Entry<string,>> entry : entrySet) {
String key = entry.getKey();
List<string>> headerValues = entry.getValue();
for (String value : headerValues) {
if (key != null && !"Server".equalsIgnoreCase(key)) {
log.debug("Response Header: " + key + ": " + value);
resp.addHeader(key, value);
}
}
}
}

This is basically the same as the request headers. In this case we just filter the Server header, where our proxy application server will add its own name.

Finally to set it up in web.xml we configure the URLs we want the proxy to handle, set the protocol, host and port and we are ready to go.