TurboManage

David Chandler's Journal of Java Web and Mobile Development

  • David M. Chandler


    Web app developer since 1994 and former Developer Advocate with Google now residing in Colorado. Besides tech, I enjoy landscape photography and share my work at ColoradoPhoto.gallery.

  • Subscribe

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 242 other followers

  • Sleepless Nights…

    March 2010
    S M T W T F S
    « Feb   Apr »
     123456
    78910111213
    14151617181920
    21222324252627
    28293031  
  • Blog Stats

    • 849,257 hits

Server-side browser detection in a servlet filter

Posted by David Chandler on March 11, 2010

Second things first: if you need a polite, pretty, and usable way to help your users upgrade their browser, point them to WhatBrowser.org, created just for that purpose.

But first you have to find out what browser they’re currently running. I’ve implemented this as a servlet filter on my signup and login pages so as to inform users as early as possible if their browser is inadequate. The basic idea is to find some part of the User-Agent header that uniquely identifies each browser. “WebKit” and “Mozilla” are out because these appear in so many browsers. Fortunately, most browsers have their common name in the User-Agent string somewhere. By default, the filter allows Chrome, Firefox, Safari, Opera, and IE versions 6-8. You can configure the filter with your own list if desired.

package com.turbomanage.gwt.server.servlet;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.inject.Singleton;

@Singleton
public class BrowserFilter implements Filter
{
	// Be default, support all GWT-capable browsers
	// Assume any version recent enough except IE
	private static final String[] DEFAULT_BROWSERS =
	{ "Chrome", "Firefox", "Safari", "Opera", "MSIE 8", "MSIE 7", "MSIE 6" };

	// Filter param keys
	public static final String KEY_BROWSER_IDS = "browserIds";
	public static final String KEY_BAD_BROWSER_URL = "badBrowserUrl";

	// Configured params
	private String[] browserIds;
	private String badBrowserUrl;

	@Override
	public void init(FilterConfig cfg) throws ServletException
	{
		String ids = cfg.getInitParameter(KEY_BROWSER_IDS);
		this.browserIds = (ids != null)?ids.split(","):DEFAULT_BROWSERS;

		badBrowserUrl = cfg.getInitParameter(KEY_BAD_BROWSER_URL);
		if (badBrowserUrl == null)
		{
			throw new IllegalArgumentException("BrowserFilter requires param badBrowserUrl");
		}
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
		throws IOException, ServletException
	{
		String userAgent = ((HttpServletRequest) req).getHeader("User-Agent");
		for (String browser_id : browserIds)
		{
			if (userAgent.contains(browser_id))
			{
				chain.doFilter(req, resp);
				return;
			}
		}
		// Unsupported browser
		((HttpServletResponse) resp).sendRedirect(this.badBrowserUrl);
	}

	@Override
	public void destroy()
	{
		this.browserIds = null;
	}
}

I’m using Guice, so this is what my configuration looks like. Of course, you can also use standard web.xml configuration instead.

package com.roa.server.guice;

import java.util.HashMap;

import com.google.inject.Singleton;
import com.google.inject.servlet.ServletModule;
import com.turbomanage.gwt.server.servlet.BrowserFilter;

public class DispatchServletModule extends ServletModule
{
	@Override
	public void configureServlets()
	{
		HashMap<String, String> filterCfg = new HashMap<String,String>();
		filterCfg.put(BrowserFilter.KEY_BROWSER_IDS, "Chrome,Firefox,Safari,Opera,MSIE 8");
		filterCfg.put(BrowserFilter.KEY_BAD_BROWSER_URL, "/s/bb.html");
		filter("/roa/signup/signup.jsp", "/roa/app/login", "/roa/app/index.html").through(
			BrowserFilter.class, filterCfg);
		...
	}
}

You initialize the filter with one or two parameters. The badBrowserUrl param is required. The filter will redirect any unsupported browser to this location. You can optionally set your own comma-separated list of browser identification strings in the browserIds param.

For further research on browser server-side identification, check out http://www.zytrax.com/tech/web/browser_ids.htm.

3 Responses to “Server-side browser detection in a servlet filter”

  1. Eric Jablow said

    Alternately, you can use GWT’s <replace-with class=”…”> gwt.xml element to drive such decisions at the login page:

    <replace-with class=”com.turbomanage.gwt.AppController.ImplIE6″>
    <when-type-is class=”com.turbomanage.gwt.AppController.Impl” />
    <when-property-is name=”user.agent” value=”ie6″ />
    </replace-with>

    Have the controller decide which display to bind to the login presenter in the inner classes Impl and ImplIE6. Create the Impl with GWT.create(Impl.class). It’s one fewer technology to use.

    • Thanks, Eric. Eventually, I may use the technique you suggest to provide alternate impls of my custom tabular data widgets which use CSS tables instead of TABLE,TD,TR, but for the beta service I’m offering, I’m simply asking users to upgrade their browser during the servlet-based signup process. We’ll see how big an obstacle this is for folks…

      • Eric Jablow said

        I did the research for this to create a RequiredText class that renders labels with a preceding asterisk, manually in IE6, and using the CSS :before selector with the content attribute in modern browsers. I ended up looking at the GWT source code to see how GWT classes used the technique.

Sorry, the comment form is closed at this time.

 
%d bloggers like this: