Groovy在list.find调用上给NPE,但仅在一段时间之后

时间:2011-07-28 21:55:11

标签: grails groovy nullpointerexception

我们有一大堆像这样的代码

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

someList可能为null,但在groovy中可以正常,因为null.find{…}工作正常。)

这行代码在grails控制器的操作中运行,并在生产中部署到服务器。经过一段时间(有时几小时,有时更长),上面的代码行将开始抛出一个NullPointerException - 一旦它开始抛出NPE,它总是抛出NPE。

通过调试,我们已经证明即使someList为null也一直正常工作(直到我们得到看似随机的第一个NPE)...也通过调试我们能够得到更详细的堆栈跟踪,表明Groovy的MetaClassRegistryImpl中存在错误.java line 214.

stack trace

我已经用谷歌搜索了我能想到的每一个组合,看看是否有任何已知的Groovy错误但没有发现任何有价值的东西。

(使用Grails 1.3.7,因此使用Groovy 1.7.8)

JMeter脚本设置为运行一系列站点交互,使此问题半可重复。该脚本将迭代50-100系列,然后错误开始出现 - 一旦出现错误,它总是出错,直到将应用程序重新部署到服务器(Glassfish)。

跟踪groovy代码看起来像这样:

//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);
}

所以NPE似乎在obj.getClass() - 如果是这样的话,我有点困惑当someList为空时它是如何工作的(但这是一个单独的主题)。

FWIW,我们 someList上进行我们自己的任何类或实例级元类编码。

Groovy中是否存在错误或者我们可能做错什么导致Groovy代码深入(随机)NPE?

UPDATE -

观察结果是someList被设置为'java null'而不是'groovy null'(NullObject)。对象来自地图(流程上下文),通过控制器动作中的流程......

class SomeController {

    def someActionFlow = {
        action {

            def someList = flow.someList

        }
    }
}

有问题的情况是,从未设置flow.someList时,它应始终为null(groovy null)。 flow只是一张地图,因此与执行flow.get('someList')

相同

上面的代码适用于未知数量的迭代,然后开始返回'java nulls'而不是'groovy nulls'。

2 个答案:

答案 0 :(得分:3)

我会冒险猜测它取决于someList的创建方式。也就是说,如果它是在Groovy中创建的

def someList = null

然后Groovy将NullObject分配给变量。但是,如果该值从其他Java组件返回为真正的Java null,那么它将抛出NPE。更进一步,Groovy / Java / JVM中可能存在一些优化,其中callsite缓存导致它始终返回NPE。

然后,这只是一个疯狂的猜测。

答案 1 :(得分:1)

修正:与GROOVY-5248(调用网站缓存缺少空检查)类似,添加接收方空检查以避免在某些情况下使用NPE

提交641c6a8d4b6b3046f4d8a1a2ac5f08f1f2769f0f