Grails 将对象插入Drools AbstractWorkingMemory时出现NullPointerException

Grails 将对象插入Drools AbstractWorkingMemory时出现NullPointerException,grails,drools,Grails,Drools,我有一个Grails应用程序,我正在从V1.3.7升级到2.3.4。我们有一些与Drools相关的功能,在1.3.7中可以使用,但在2.3.4中无法使用。我不太确定问题出在哪里,因为Drools依赖关系没有改变,我也不能很好地理解我所遇到的错误,所以我希望有人能解释一下可能导致这个错误的原因 下面是一段代码,它正在爆炸: def session = ruleBase.newStatefulSession() // put objects in session objList.each {

我有一个Grails应用程序,我正在从V1.3.7升级到2.3.4。我们有一些与Drools相关的功能,在1.3.7中可以使用,但在2.3.4中无法使用。我不太确定问题出在哪里,因为Drools依赖关系没有改变,我也不能很好地理解我所遇到的错误,所以我希望有人能解释一下可能导致这个错误的原因

下面是一段代码,它正在爆炸:

 def session = ruleBase.newStatefulSession()

 // put objects in session
 objList.each {obj ->
     session.insert(obj)
 }
objList的内容是此类型的单个复合对象:

public class ConsumerPrequalificationComposite extends PrequalificationComposite {
InsuranceApplicationPerson insAppPerson
List<Person> applicants

public boolean isDisabled(String questionKey, String answer) {
    return getAnswerToQuestion(questionKey).equals(answer);
}

String getAnswerToQuestion(String questionTypeOrUserDefinedKey) {
    if (!person) {
        return null
    }

    def answerList = person.answers ? person.answers : insAppPerson.answers

    if (answerList) {
        return getAnswerToQuestion(answerList, questionTypeOrUserDefinedKey)
    } else {
        return null
    }
}

@Override
boolean benefitIncludesIndividualYoungerThanOnRequestedEffectiveDate(int years, int months, int days) {
    if (super.benefitIncludesIndividualYoungerThanOnRequestedEffectiveDate(years, months, days)) {
        return true
    }

    for (Person applicant in applicants) {
        if (onDateWillBeYoungerThan(applicant, this.requestedEffectiveDate, years, months, days)) {
            return true
        }
    }

    return false
}

boolean isSingleApplicant() {
    return applicants?.size() == 1
}
}

abstract class PrequalificationComposite extends DroolsComposite {
List planRuleHandles = []
Map<InsuranceType, Date> requestedEffectiveDates = [:]
Map insTypeFailureReasons = [:]
List selectedInsuranceTypes = []
String agentId
boolean validLocation
List<PaymentDurationType> paymentDurationTypes = []

// Added these because grails adds a get for booleans and drools looks for a is
boolean isValidLocation() {
    return this.validLocation
}

boolean getValidLocation() {
    return this.validLocation
}

void setValidLocation(boolean isValid) {
    this.validLocation = isValid
}

void addPlan(String ruleHandle){
    planRuleHandles << ruleHandle
}

void addPaymentDurationType(PaymentDurationType paymentDurationType) {
    paymentDurationTypes.add(paymentDurationType)
}

void addFailureReason(InsuranceType insType, ContentKey contentKey) {
    insTypeFailureReasons.put(insType, contentKey)
}

boolean hasFailureReason(InsuranceType insuranceType) {
    return insTypeFailureReasons.get(insuranceType) != null
}

String getAnswerToQuestion(String questionTypeOrUserDefinedKey){
    if(!person){
        return null
    }

    return getAnswerToQuestion(person.answers, questionTypeOrUserDefinedKey)

}

Date getAnswerToQuestionAsDate(String questionTypeOrUserDefinedKey){
    def answer = getAnswerToQuestion(questionTypeOrUserDefinedKey)
    if (answer) {
        Date dateAvailable = DateUtil.parse(answer)
        return dateAvailable
    }

    return null
}

boolean olderThanOnJanOneOfRequestedEffectiveDateForInsuranceType(InsuranceType insType, int years, int months, int days) {
    def requestedEffectiveDate = requestedEffectiveDates[insType] ?: new Date()
    def cal = Calendar.getInstance()
    cal.setTime(requestedEffectiveDate)
    cal.set(Calendar.MONTH, 0)
    cal.set(Calendar.DATE, 1)
    def janOne = cal.getTime()
    onDateWillBeOlderThan(janOne, years, months, days)  // have to add 1 to the year becuase needs to be "older than"
}

boolean youngerThanOnRequestedEffective(int years, int months, int days) {
    onDateWillBeYoungerThan(this.requestedEffectiveDate, years, months, days)
}

boolean olderThanOnRequestedEffective(int years, int months, int days) {
    onDateWillBeOlderThan(this.requestedEffectiveDate, years, months, days)
}

boolean olderThanOnRequestedEffectiveForInsuranceType(InsuranceType insType, int years, int months, int days) {
    onDateWillBeOlderThan(getEffectiveDate(insType), years, months, days)
}

boolean heightWeightAgeValid(int minYears, int minMonths, int maxYears, int maxMonths, int minHeight, int maxHeight, int minWeight, int maxWeight) {
    def result = false
    if (olderThanNow(minYears, minMonths, 0) && !olderThanNow(maxYears, maxMonths, 0)) {
        if (person.height > minHeight && person.height < maxHeight && person.weight > minWeight && person.weight < maxWeight) {
            result = true
        }
    }
    return result
}

public void setState(String state) {
    person.address.state = state
}

boolean checkEffectiveDates(InsuranceType insType, String dateAvailableString) {
    def requestedEffectiveDate = this.requestedEffectiveDates[insType] ?: new Date()
    def dateAvailable = DateUtil.parse(dateAvailableString)
    return (dateAvailable.equals(requestedEffectiveDate) || dateAvailable.before(requestedEffectiveDate)) 
}

public abstract boolean isDisabled( String questionKey, String answer );

boolean hasSelectedInsuranceType(InsuranceType insType) {
    return selectedInsuranceTypes.any { it == insType }
}

String getAgentId() {
    return agentId
}

Date getEffectiveDate(InsuranceType insType) {
    Date requestedEffectiveDate = this.requestedEffectiveDates[insType] ?: new Date()
    return requestedEffectiveDate
}

/**
 * Checks all individuals to be covered on the benefit and returns true if any of them are/will be younger than the
 * specified age as of the requested effective date
 */
boolean benefitIncludesIndividualYoungerThanOnRequestedEffectiveDate(int years, int months, int days) {
    if (person && onDateWillBeYoungerThan(person, this.requestedEffectiveDate, years, months, days)) {
        return true
    }

    for (InsuranceApplicationPlanPerson planPerson in insuranceApplicationPlan?.insuranceApplicationPlanPersons) {
        if (onDateWillBeYoungerThan(planPerson.insuranceApplicationPerson.person, this.requestedEffectiveDate, years, months, days)) {
            return true
        }
    }
    return false
}

boolean isFfm() {
    return person.ffmEligibilityInfo != null
}

boolean isQhpEligibility() {
    return person.ffmEligibilityInfo ? person.ffmEligibilityInfo.qhpEligibilityIndicator : false
}

boolean isCsrEligibility() {
    return person.ffmEligibilityInfo ? person.ffmEligibilityInfo.csrEligibilityIndicator : false
}

}

abstract class DroolsComposite {

InsuranceApplicationPlan insuranceApplicationPlan
Person person
Date requestedEffectiveDate

String getAnswerToQuestion(Collection<Answer> answers, String questionTypeOrUserDefinedKey) {

    def answerString = null
    def answer = null
    try{
        def questionType = QuestionType.valueOf(QuestionType.class, questionTypeOrUserDefinedKey)
        answer = answers.find {
            it.question.questionType == questionType
        }
    } catch (Exception e){
        Logger.getLogger(this.class).debug("QuestionType could not be determined for $questionTypeOrUserDefinedKey this is not an error.  The value could be referencing a question user defined key value")
    }

    if (!answer){
        answer = answers.find {
            if (it.question == null) {
                println "OH MY GOLLY!"
            }
            it.question.userDefinedKey.equals(questionTypeOrUserDefinedKey)
        }
    }

    if (answer) {
        if (answer.question.multiListAnswerType == PossibleAnswerType.USER_CONFIGURED) {
            def possibleAnswer = answer.question.possibleAnswers.find {
                it.id.toString().equals(answer.stringValue) 
            }
            answerString = possibleAnswer.userDefinedKey
        } else {
            answerString = answer.toString()
        }
    }
    return answerString

}

boolean olderThanNow(int years, int months, int days) {
    onDateWillBeOlderThan(new Date(), years, months, days)
}

boolean onDateWillBeOlderThan(Date date, int years, int months, int days) {
    def result = false
    def target = Calendar.getInstance()
    target.setTime(date)
    target.add(Calendar.HOUR, 1)
    def ageCal = Calendar.getInstance()
    ageCal.setTime(person.dateOfBirth)
    ageCal.add(Calendar.YEAR, years)
    ageCal.add(Calendar.MONTH, months)
    ageCal.add(Calendar.DAY_OF_MONTH, days)

    if (ageCal.before(target)) {
        result = true
    }

    return result
}

boolean youngerThanNow(int years, int months, int days) {
    onDateWillBeYoungerThan(new Date(), years, months, days)
}

boolean onDateWillBeYoungerThan(Date date, int years, int months, int days) {
    def result = false
    def target = Calendar.getInstance()
    target.setTime(date)
    def ageCal = Calendar.getInstance()
    ageCal.setTime(person.dateOfBirth)
    ageCal.add(Calendar.YEAR, years)
    ageCal.add(Calendar.MONTH, months)
    ageCal.add(Calendar.DAY_OF_MONTH, days)

    if (ageCal.after(target)) {
        result = true
    }

    return result
}

boolean onDateWillBeYoungerThan(Person personToCheck, Date date, int years, int months, int days) {
    Calendar targetDate = Calendar.getInstance()
    targetDate.setTime(date)

    Calendar dateAgeReached = Calendar.getInstance()
    dateAgeReached.setTime(personToCheck.dateOfBirth)
    dateAgeReached.add(Calendar.YEAR, years)
    dateAgeReached.add(Calendar.MONTH, months)
    dateAgeReached.add(Calendar.DAY_OF_MONTH,days)

    return dateAgeReached.after(targetDate)
}

boolean olderThanOnJanOneOfRequestedEffective(int years, int months, int days) {
    def cal = Calendar.getInstance()
    cal.setTime(this.requestedEffectiveDate)
    cal.set(Calendar.MONTH, 0)
    cal.set(Calendar.DATE, 1)
    def janOne = cal.getTime()
    onDateWillBeOlderThan(janOne, years, months, days)  // have to add 1 to the year becuase needs to be "older than"
}

boolean getDisability() {
    return insuranceApplicationPlan != null ? insuranceApplicationPlan.policyOwner?.insuranceApplicationPerson?.disability : null
}

Relationship getRelationshipToOwner() {
    return person.relationshipToOwner
}

Date getRequestedEffectiveDate() {
    return requestedEffectiveDate
}

public InsuranceApplicationPlan getInsuranceApplicationPlan() {
    return insuranceApplicationPlan
}

public Date getDateOfBirth() {
    return person.dateOfBirth
}

public String getCsrLevel() {
    return person?.ffmEligibilityInfo?.csrLevel ?: null
}

public Integer getRequestedEffectiveDateYear() {
    Calendar cal = Calendar.getInstance()
    if (getRequestedEffectiveDate() != null) {
        return 0
    }
    cal.setTime(insuranceApplicationPlan.getEffectiveDateOrRequestedIfNull())
    return cal.get(Calendar.YEAR)
}

}
在会话中插入其他对象时,它似乎确实起作用,但我尝试插入的这个特定对象并没有什么特别之处,只是它扩展了另一个抽象类,而这个抽象类又扩展了另一个抽象类。但是,所有这些都可以在Grails1.3.7中使用。我已经试过调试Drools源代码,但是我也没有得到任何好的线索


我使用的是Drools 6.0.0.Final

对象列表中有什么?你必须提供详细信息,因为这是问题的根源。我在描述中添加了类及其扩展的类。我在这里没有看到奇怪的东西。。。也许是关于你的规则吧。插入其他事实不会生成此异常,因此您知道使用特殊对象触发哪些规则吗?也许其中有一些eval(…)在运行时创建空指针,因为对象的一个字段为空,而规则试图在其上获取一个字段。嗯,是的,你是对的。我将规则文件简化为一个超基本的文件,然后它就可以工作了,所以它肯定与此相关。奇怪的是,这个完全相同的文件仅通过使用较旧版本的Grails工作。我猜一定是有什么东西在类路径上结束了,这是在扔东西。我将尝试慢慢地将规则添加回规则文件,看看是否可以找出导致事情爆炸的具体规则。@zenbeni非常感谢!我能够找到导致问题的规则,我应该能够很快解决它。
java.lang.NullPointerException
    at org.drools.core.rule.PredicateConstraint.isAllowed(PredicateConstraint.java:299)
    at org.drools.core.reteoo.AlphaNode.assertObject(AlphaNode.java:134)
    at org.drools.core.reteoo.CompositeObjectSinkAdapter.doPropagateAssertObject(CompositeObjectSinkAdapter.java:502)
    at org.drools.core.reteoo.CompositeObjectSinkAdapter.propagateAssertObject(CompositeObjectSinkAdapter.java:387)
    at org.drools.core.reteoo.ObjectTypeNode.assertObject(ObjectTypeNode.java:288)
    at org.drools.core.reteoo.EntryPointNode.assertObject(EntryPointNode.java:260)
    at org.drools.core.common.NamedEntryPoint.insert(NamedEntryPoint.java:360)
    at org.drools.core.common.NamedEntryPoint.insert(NamedEntryPoint.java:279)
    at org.drools.core.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:1149)
    at org.drools.core.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:1093)