Groovy дать NPE на вызов list.find, но только через некоторое время - PullRequest
2 голосов
/ 29 июля 2011

У нас есть кусок кода что-то вроде этого

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

(someList может быть нулевым, но это нормально в groovy, так как null.find{…} работает нормально.)

Эта строка кода выполняется в действии контроллера Grails и развернута в производственном процессе на сервере. Через определенный промежуток времени (иногда часы, иногда дольше) вышеприведенная строка кода начнет выдавать исключение NullPointerException - и как только он начинает выбрасывать NPE, он всегда выбрасывает NPE.

Посредством отладки мы доказали, что он работает нормально, даже когда someList имеет значение null (вплоть до получения, казалось бы, случайного первого NPE)… также благодаря отладке мы смогли получить более подробную трассировку стека, которая указала, что ошибка была в MetaClassRegistryImpl Groovy. .java строка 214.

stack trace

Я погуглил каждую комбинацию, которую могу придумать, чтобы увидеть, есть ли какие-либо известные ошибки Groovy, но не нашел ничего ценного.

(используется Grails 1.3.7, то есть Groovy 1.7.8)

Сценарий JMeter был настроен для запуска серии взаимодействий сайта, что делает эту проблему практически повторяемой. Сценарий будет проходить по сериям 50-100, а затем начинает появляться ошибка - как только ошибка появляется, она всегда находится в ошибке, пока приложение не будет повторно развернуто на сервере (Glassfish).

Трассировка через отличный код выглядит примерно так:

//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 или что мы можем делать неправильно, чтобы вызвать (случайный) NPE глубоко в коде Groovy?

update-

Наблюдение состоит в том, что someList устанавливается в значение 'java null' вместо 'groovy null' (NullObject). Объект приходит с карты (контекст потока) через поток в действии контроллера ...

class SomeController {

    def someActionFlow = {
        action {

            def someList = flow.someList

        }
    }
}

Речь идет о том, что когда flow.someList никогда не задавался, он всегда должен быть нулевым (groovy null). flow - это просто карта, так же как и flow.get('someList')

Приведенный выше код отлично работает для неизвестного числа итераций, а затем начинает возвращать 'java nulls' вместо 'groovy nulls'.

Ответы [ 2 ]

3 голосов
/ 29 июля 2011

Я рискну предположить, что это зависит от того, как создается someList. То есть, если он создан в Groovy как

def someList = null

Затем Groovy назначает NullObject переменной. Тем не менее, если значение возвращается из какого-либо другого компонента Java как настоящая Java null, то оно будет выбрасывать NPE. В дальнейшем может произойти некоторая оптимизация в Groovy / Java / JVM, где кеширование места вызова заставляет его всегда возвращать NPE.

Опять же, это просто дикое предположение.

1 голос
/ 14 августа 2014

Исправлено: аналогично GROOVY-5248 (в кэше сайта вызовов отсутствует проверка нуля), добавьте проверку нуля получателя, чтобы избежать NPE при определенных обстоятельствах

commit 641c6a8d4b6b3046f4d8a1a2ac5f08f1f2769f0f

...