Grails:验证域对象时的'methodInvocation'错误代码

时间:2013-06-07 12:50:26

标签: spring grails gorm

两个星期后,出现了一个疯狂的问题! 它只出现在生产中(不是开发,集成,预生产模式)和下班后....

症状:一旦发生“某事”,用户就无法注册了。经过数天和数天的调查,我不知道如何重现这个问题。 目前,我们有一个丑陋的解决方法来重启tomcat实例,但这是非常丑陋。

有关信息,两次发货之间的差异与报名流程无关。

我认为这是相关的:

域名:

class UserAccount implements Serializable {
    static transients = ['tokenLogin']
    String email
    String pwd
    Date creationDate = new Date()
    String tokenLogin


    //[skip] dozen of other attributes

    static constraints = {
        email(matches:EmailRegexp,blank:false)
        pwd(blank:false)
        tokenLogin(editable:false)
    }

    public void setEmail(String email) {
        this.email = email
        authId = authenticationChannel.getUsername(email)
        // We must recalculate token login
        this.tokenLogin = generateTokenLogin(email, creationDate, pwd)
    }

    public void setPwd(String pwd) {
        this.pwd = pwd
        // We must recalculate token login
        this.tokenLogin = generateTokenLogin(email, creationDate, pwd)
    }

    public static String generateTokenLogin(String email, Date creationDate, String pwd) {
        // work well
        // even tested when return null or with NPE in it and does not produce the same log
        generated = // work with MessageDigest, MD5, salt, etc. (not disclosed, because we are currently under security audit)
        return generated;
    }
}

控制器:

def saveAccount = {
    def user = new UserAccount(email: params.email?.trim(), pwd: params.pwd) // More parameters, but here what is relevant
    user.validate()
    if(user.hasErrors()) log.info("${user.errors}") // added since the issue appears
    // Other verification (unicity, clear password verification, etc.)
    // Password encryption
    // user.save()
}

输出日志:

2013-06-03 15:37:32,165 [TP-Processor46] INFO  controller.UserAccountController  - org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'domain.UserAccount' on field 'pwd': rejected value [foobar]; codes [methodInvocation.domain.UserAccount.pwd,methodInvocation.pwd,methodInvocation.java.lang.String,methodInvocation]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [domain.UserAccount.pwd,pwd]; arguments []; default message [pwd]]; default message [Property 'pwd' threw exception; nested exception is java.lang.NullPointerException]

问题:

  • 如何显示此输出?我们在pwdemail字段中看到了此输出,而从未见过其他字段。
  • “methodInvocation”代码的含义是什么? (我在spring-context中看到MethodInvocationException捕获异常时抛出BeanWrapperImpl.setPropertyValue
  • 如何获取更多信息? (现在,我们运行一个修补的spring-context.jar,显示BeanWrapperImpl中的StackTrace,但直到现在才能重现)

信息:

  • Grails 1.3.7 =>春天3.0.5-GA
  • Mysql jdbc
  • Tomcat 6

start.sh(tomcat选项)

JAVA_OPTS="-Dapp=prod -Xms1024m -Xmx1024m -XX:MaxPermSize=256m \
-Dfile.encoding=UTF-8 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC \
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled \
-XX:+HeapDumpOnOutOfMemoryError \
-Xloggc:/path/to/gc.log"

编辑: 我们更改并编译spring-context.jar以便能够看到StackTrace。我们得到这个:

org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessException details (1) are:
PropertyAccessException 1:
org.springframework.beans.MethodInvocationException: Property 'pwd' threw exception; nested exception is java.lang.NullPointerException
        at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1127)
        at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:900)
        at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:76)
        at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:673)
        at org.springframework.validation.DataBinder.doBind(DataBinder.java:569)
        at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:191)
[snip]
Caused by: java.lang.NullPointerException
        at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.checkCall(PojoMetaClassSite.java:54)
        at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:42)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:124)
        at domain.UserAccount.generateTokenLogin(UserAccount.groovy:369)
        at domain.UserAccount$generateTokenLogin.callStatic(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:48)
        at domain.UserAccount$generateTokenLogin.callStatic(Unknown Source)
        at domain.UserAccount.setPwd(UserAccount.groovy:184)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1114)

根本原因是在某些奇怪的条件下存在creationDate可能null。我想解决方法不是在setter中调用generateTokenLogin()(并且只在getter中调用)。

我无法解释为什么我们之前没有这个问题(没有最近的升级等)

3 个答案:

答案 0 :(得分:0)

听起来好像一旦发生这种情况就不会在没有重启的情况下修复。我会看看你的应用程序的permgen大小。如果它适用于每个环境,那么期望生产就可能是内存问题。

简单的测试方法是添加200megs左右开始。

答案 1 :(得分:0)

只是一个疯狂的猜测 - 当你覆盖一个setter时,你不应该在这个setter中设置该字段的值,因为它是一种递归,或者至少是不可预知的行为(如此)可能是预期的,也许可以试试使用原始字段访问运算符:

public void setPwd(String pwd) {
    this.@pwd = pwd
    // We must recalculate token login
    this.tokenLogin = generateTokenLogin(email, pwd)
}

email字段/ setter

也是如此

答案 2 :(得分:0)

您的pre-prod服务器与您的prod服务器完全相同吗? 你做的未成年人修改是哪些,你有没有影响?

可能是生成的战争包含错误,您是否尝试回滚修改?

Mayby你可以接受你的生产战争并把​​它放到你的预先制作的环境中来重现这个问题。

是否有来自插件,grails版本或依赖项中的任何内容的最新更新?

没有回答你的问题,但是我可以给你调查意见。

施奈特