Monday, May 24, 2010

Writing unit tests when using GWT's Static String Internationalization (I18n) feature

The Google Web Toolkit (GWT) has a fairly simple infrastructure for managing internationalization. While there are a number of different options, the easiest one to use is called Static String Internationalization The basic idea is that you create a properties file for each language and GWT's deferred binding process creates an instance of a Interface which will be loaded depending on the locale. It works something like this:

  1. Create a properties file (e.g. FooConstants.properties). This will contain definitions of the form bar=A string that I want to i18n.
  2. Use the i18nCreator script to generate the Interface definition: i18nCreator -eclipse Foo com.example.foo.client.FooConstants
  3. In your code call GWT.create() to instantiate an instance. e.g:
    public static class MessageDisplayer {
        public MessageDisplayer(Alerter alerter) {
            FooConstants constants = (FooConstants) GWT.create(FooConstants.class);
                alerter.alert(FooConstants.bar());
            }
        }
    }
    
The problem with this is that calling GWT.create from within your code makes it difficult to unit test. If your code calls this directly then you will have to create your unit tests using GWT's Junit3 hack. Running unit tests this way is very slow, and I find it much better to try and factor out as much GWT specific code as possible so that you can write normal boring tests (for example using JUnit 4, or using mocks, or whatever else that GWT tests don't support that takes your fancy). The trick is that if you have any code that relies on an instance of these Constants/Messages files then you are screwed. The solution is to use a DynamicProxy to generate an instance of the interface and then inject these into your code. First we need to refactor our class to have the Constants interface injected, e.g:
public static class MessageDisplayer {
    public MessageDisplayer(Alerter alerter, FooConstants constants) {
        alerter.alert(FooConstants.bar());
    }
}
Next we need to write some code to generate the constants. When the i18nCreator generates the interface it helpfully annotates it with the default text it needs. We can exploit this to generate an instance for testing:
public class ConstantsMocker implements InvocationHandler {
     @SuppressWarnings("unchecked")
     public static <T> T get(Class<? extends T> i18nInterface) {
         return (T) Proxy.newProxyInstance(i18nInterface.getClassLoader(),
                  new Class<?>[] { i18nInterface },
                  new ConstantsMocker());
     }
     public static final String NO_DEFAULT_MESSAGE = "[No Default Message Defined]";

     @Override
     public Object invoke(Object proxy, Method method, Object[] args)
             throws Throwable {
         DefaultMessage message = method.getAnnotation(DefaultMessage.class);
         if (message == null) {
             return NO_DEFAULT_MESSAGE;
         }
         return message.value();
     }
} 
Now when we write our test we can pass down an instance, e.g
@Test
    public void whenConstructorIsCalledAlerterAlertIsCalled {
        AtomicBoolean wasAlerted = new AtomicBoolean(false);
        FooConstants fooConstants = ConstantsMocker.get(FooConstants.class);
        final String expected = fooConstants.bar();
        Alerter alerter = new Alerter() {
            void alert(String msg) {
                assertEquals(expected, msg);
                wasAlerted.set(true);
            }
        }
        new MessageDisplayer(alerter, fooConstants);
        assertTrue(wasAlerted.get());
    }
Whilst this is a toy example it can be a very useful technique if used carefully. Note that regular GWT code just injects the instance from GWT.create() as follows:
MessageDisplayer displayer = 
      new MessageDisplayer(Alerter, (FooConstants)GWT.create(FooConstants.java));

Sunday, May 16, 2010

Creating a bounded LRU Cache with LinkedHashMap

I recently had cause to implement a fixed size cache in some code was writing. I wanted a straightforward map, but with a fixed size and the ability to evict on a least-recently-used (LRU) basis. I thought quickly to get something up and running that I would use a LinkedHashMap and then have my get and put methods use the ordering to implement the LRU policy.

Well it turns out that LinkedHashMap already has this support built-in if you know what you're doing. There are two parts of this jigsaw:

  1. Override the protected removeEldestEntry method which returns a boolean if the eldest entry should be removed. This method is called for every put with the element that
    is eldest according the LRU policy.
  2. Call the LinkedHashMap(int capacity, float loadFactor, boolean accessOrder) constructor. Specifying true for accessOrder means that the order of the elements will be sorted in the order they were last accessed (from least recently used to most recently used).
Putting this together yields:
public class BoundedLruCache<KEY, TYPE> extends LinkedHashMap<KEY, TYPE> {

    private static  final int DEFAULT_INITIAL_CAPACITY = 100;

    private static final float DEFAULT_LOAD_FACTOR = 0.75;

    private final int bound;

    public BoundedLruCache(final int bound) {
        super(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, true);
        this.bound = bound;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<KEY, TYPE> eldest) {
        return size() > bound;
    }
}
Which is remarkably compact. Once again the JRE library proves to have some nice bits and pieces if you know where to look.