2

We have a chunk of code something like this

// semi-pseudo code
def result = someList.find { condition == true }

(someList may be null, but that is ok in groovy as null.find{…} works fine.)

This line of code is running in an action of a grails controller and deployed in production to a server. After a period of time (sometimes hours, sometimes longer) the above line of code will start throwing a NullPointerException — and once it starts throwing the NPE it always throws the NPE.

Through debugging we've proven that it works fine even when someList is null (up until we get the seemingly random first NPE)… also through debugging we were able to get a more detail stacktrace that indicated there error was in Groovy's MetaClassRegistryImpl.java line 214.

stack trace

I've googled every combination I can think of to see if there are any known Groovy bugs but found nothing of value.

(It is using Grails 1.3.7, thus Groovy 1.7.8)

A JMeter script was setup to run through a series of site interactions that makes this problem semi-repeatable. The script will iterate through 50-100 series and then the error starts appearing - once the error appears it is always in error until the application is redeployed to the server (Glassfish).

Tracing through the groovy code it looks something like this:

//AbstractCallSite.java
public Object call(Object receiver, Object arg1) throws Throwable {
    return call(receiver, ArrayUtil.createArray(arg1));
}

//PerInstancePojoMetaClassSite.java
public Object call(Object receiver, Object[] args) throws Throwable {
    if (info.hasPerInstanceMetaClasses()) {
      try {
          return InvokerHelper.getMetaClass(receiver).invokeMethod(receiver, name, args);
      } catch (GroovyRuntimeException gre) {
          throw ScriptBytecodeAdapter.unwrap(gre);
      }
    } else {
      return CallSiteArray.defaultCall(this, receiver, args);
    }
}


//InvokerHelper.java
public static MetaClass getMetaClass(Object object) {
    if (object instanceof GroovyObject)
        return ((GroovyObject) object).getMetaClass();
    else
        return ((MetaClassRegistryImpl) GroovySystem.getMetaClassRegistry()).getMetaClass(object);
}

//MetaClassRegistryImpl.java
public MetaClass getMetaClass(Object obj) {
    return ClassInfo.getClassInfo(obj.getClass()).getMetaClass(obj);
}

So it appears the NPE is on the obj.getClass() — if that's the case I'm a little baffled how it ever works when someList is null (but that is a separate topic).

FWIW, we are not doing any class or instance level meta-class coding of our own on someList.

Is there a bug in Groovy or what could we possibly be doing wrong to cause a (random) NPE deep in Groovy code?

UPDATE—

The observation is that someList is being set to a 'java null' instead of a 'groovy null' (NullObject). The object is coming from a map (flow context) via a flow in a controller action...

class SomeController {

    def someActionFlow = {
        action {

            def someList = flow.someList

        }
    }
}

The case in question is when flow.someList has never been set it should always be null (groovy null). flow is just a map so it is the same as doing flow.get('someList')

The above code works fine for an unknown number of iterations and then starts returning 'java nulls' instead of 'groovy nulls'.

k s
  • 882
  • 8
  • 9
  • Also fwiw, `def result = someList?.find { condition == true }` makes the problem go away. So, while the safe navigation operator seems to be the solution, there is a lot of already in-production code that needs to be refactored due to this issue with Groovy's NullObject. – k s Jul 28 '11 at 22:03
  • related: http://stackoverflow.com/questions/5472795/groovy-why-i-dont-get-a-nullpointerexception-in-this-case – k s Jul 28 '11 at 22:03

2 Answers2

3

I'm going to hazard a guess that it's dependent on how someList is created. That is, if it's created in Groovy as

def someList = null

Then Groovy assigns the NullObject to the variable. However, if the value is returned from some other Java component as a real Java null, then it will throw the NPE. Going further, there might be some optimization in Groovy/Java/JVM where callsite caching is causing it to always return NPE.

Then again, this is just a wild guess.

Phuong LeCong
  • 1,834
  • 16
  • 19
  • I'm also on this project and what we found was exactly that. `println someList.getClass()` resulted in `NullObject` until of course the NPE was thrown in which case it is the java `null`. This we knew, but we don't know _why_ or _how_ this object is transforming from `NullObject` to `null`. – Matthew Boston Jul 29 '11 at 14:39
  • So then the question is how does it get to become a "Java null" vs a "Groovy null". – k s Aug 01 '11 at 16:17
  • I'm checking this as the answer even though we have no idea what the underlying issue is with groovy/grails switching from returning NullObjects from the flow context map to returning actual java nulls for the same call. – k s Aug 03 '11 at 23:32
1

Fixed: Similarly to GROOVY-5248 (call site caching missing null check), add a receiver null check to avoid NPEs under certain circumstances

commit 641c6a8d4b6b3046f4d8a1a2ac5f08f1f2769f0f

kregor
  • 11
  • 2