Monthly Archives: May 2011

Unexpected side effect of LambdaJ aggregates

I have been using LambdaJ (2.3.2) for a couple of weeks now. It’s a simple yet impressive API that you should read more about if you’re not using it already.

One of the features of LamdaJ are aggregates, with which you can do stuff like the following. Assuming a class

class Person {

  // name etc ...  

  int age;

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

}

you could then do

Person twelveYearsOld = new Person();
twelveYearsOld.setAge(12);
Person fiftyYearsOld = new Person();
fiftyYearsOld.setAge(50);
List<Person> persons = Array.asList(twelveYearsOld, fiftyYearsOld);

int maxAge = maxFrom(persons).getAge(); // Will be 50
int minAge = maxFrom(persons).getAge(); // Will be 12
int sumAge = sumFrom(persons).getAge(); // Will be 62

The other day I used sumFrom() for the very first time, in a layer of our application where we do not (yet) apply TDD. When I ran a manual test of the changed code, I got an exception informing me that updates could not be made in a read-only transaction! Well yes, the transaction surrounding the code that I changed was read-only, but I hadn’t made any updates to my Hibernate entities…? It didn’t take too long to narrow it down to the LambdaJ sumFrom() being what triggered the exception (which was thrown in the Hibernate flush made at transaction commit). It took me a while longer to understand what was going on. It was a bit interesting, so I will try to explain it to you.

LambdaJ uses proxies for some of its functionality, such as aggregates. If the type argument of the collection (Person in the above example) is an interface, a regular Java Proxy is used. If however the type argument is a class – as in our case above – then LamdaJ will make use of cglib which will perform runtime bytecode instrumentation. This results in a “secret” subclass of the type argument class, for which method calls are sent to a MethodInterceptor which works just like an InvocationHandler for a regular interface based Proxy (LambdaJ’s InvocationInterceptor implements both interfaces).

In the case of LambdaJ aggregates, any method calls to the proxy will be invoked on all the objects in the collection, and the return values will be assembled by some Aggregator (min, max, average, sum etc).

This still does not explain the behavior I was seeing, does it? No, because I left out a part, which I finally realized to be the explanation. In my case the data class had properties that were initialized with default values, not at the declaration and not with a simple assignment, but with a caller to setters in the default constructor. That is, as if the Person class above had

class Person {

  ...

  public Person() {
    this.setAge(20); // Set default age to 20 ("this" is explicit for clarity)
  }

  ...

}

Since cglib proxies are subclasses of the original class, it means that creating an instance of this proxy class will invoke the default constructor of it’s base class. If there are method calls from within the constructor on the object itself, these method calls will also be taken care of by the MethodInterceptor. In the case of LambdaJ, this means that the method call in the constructor will be issued on all the objects in the collection…

Reusing the example above, with Person having it’s new constructor, the result is this

List<Person> persons = Array.asList(twelveYearsOld, fiftyYearsOld);

int maxAge = maxFrom(persons).getAge(); // Will be 20!
int minAge = maxFrom(persons).getAge(); // Will be 20!
int sumAge = sumFrom(persons).getAge(); // Will be 20!
int age12 = twelveYearsOld.getAge(); // Will be changed to 20!!!
int age50 = fiftyYearsOld.getAge(); // Will be changed to 20!!!

The workaround in this case is very simple: Don’t call setters from the constructor. Instead use “inline” property assignment (this.age = 20).

Unfortunately, in our project we have lot’s of classes with default values set by calling setters in the constructor. Therefore I created a patch for LambdaJ, which “deactivates” the InvocationInterceptor (or rather the concrete ProxyIterator subclass) while cglib proxy creation is in progress, and activates it before the proxy is returned for use. Hopefully the fix will make it into LambdaJ 2.3.3.