Disclaimer

The views and opinions expressed in this blog are my own and do not necessarily reflect those of my employer. The views and opinions expressed by visitors to this blog are theirs and do not necessarily reflect my own

Tuesday, June 21, 2011

GWT : DisclosurePanel Images, IE6 and SSL

I recently worked on a project where we were using standard GWT Disclosure Panels and Tree components. When the application was sitting behind SSL we found that the arrow images were not visible in IE6. What was more frustrating was that in every other browser the images displayed correctly.

After a lot (and I mean a lot) of time I stumbled across the API documentation for image bundles which is the mechanism/technique Google have used to package up images for widgets into a composite image that help reduce calls to the server and speed up the user experience.
In summary, the problem is that GWT makes use an activeX control to process png transparency on IE6. Internet Explorer specifies that files which require the plugin for viewing must be cached to disk by the browser. By default the HTTP headers set by the GWT servlets when the image is requested specify that the payload should not be cached. The image then never stored to disk (cached) and the activeX control cannot process the image and display it to the page.

The solution is actually quite simple. You need to create an additional servlet that executes when image resources are requested. This is done by setting up a servlet in the web.xml with a file filter.

package xxxx.xxxxx.xxxxx;

public class ImageServlet extends HttpServlet {

 private boolean isIE6(final HttpServletRequest request){
  final String userAgent = ((HttpServletRequest) request).getHeader("User-Agent");
  return userAgent.contains("MSIE 6");
 }

 protected doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {

  if (isIE6(request) ){
   response.setHeader("Cache-Control", "");
   response.setHeader("Pragma", "" );

   // This should be some date in the future. Dynamically create the
   // date part of the string to be something in the future e.g.
   // sysdate + 1
   response.setHeader("Expires","Thu, 1 Jan 2013 00:00:00 ");
  }

  final String uri = request.getRequestURI();

  // Extract the icon name
  final String iconName = uri.substring(request.getContextPath().length(), uri.length() );

  // Supply the icon back to the browser
  continueProcessing(request, response, iconName);
 }

 protected void continueProcessing(final HttpServletRequest request
    , final HttpServletResponse response
    , final String iconName){

  final ServletContext context = getServletContext();
  final InputStream is  = context.getResourceAsStream(iconName);
  final OutputStream os = response.getOutputStream();
  response.setContentType("image/png");

  byte[] = new byte[8912];

  while (true){
   final int l = is.read(b);
   if (l < 0)
    break;
   os.write(b,0,l);
  }
 }
}
The servlet itself should look something like that below :

 ImageServlet
 xxxx.xxxxx.xxxxxx.ImageServlet


 ImageServlet
 *.png


You then need to set up the servlet mapping in your web.xml so that all png requests get routed through this new servlet. The example could be improved by adding caching of images requested. This would also speed up load performance of your application for images that are requested more than once.

No comments: