NPE in Expressions.java doing model validation
matt.drees Feb 23, 2007 5:40 PMMy app needs to render a list of questions whose type is database-driven. I'm using a few facelets TagHandlers to accomplish this. The view looks like this:
<s:validateAll> <f:facet name="aroundInvalidField"> <s:span styleClass="errors"/> </f:facet> <h:panelGrid id="questionPanel" columns="3"> <crs:customQuestions registrationType="#{registrationType}" questionTextVar="questionText" questionNumberVar="i" requiredVar="required"> <h:outputLabel for="answerInput#{i}" value="#{i}" id="questionNumber#{i}"/> <s:formattedText value="#{questionText}" id="questionText#{i}"/> <h:panelGroup> <s:decorate> <crs:isText> <h:inputText id="answerInput#{i}" value="#{registrationProcess.pageAnswers[ i].valueString}" required="#{required}"> <a:support event="onblur" reRender="questionErrors#{i}"/> </h:inputText> </crs:isText> <crs:isDate> <h:inputText id="answerInput#{i}" value="#{registrationProcess.pageAnswers[ i].valueDate}" required="#{required}"> <s:convertDateTime pattern="MM/dd/yyyy"/> <a:support event="onblur" reRender="questionErrors#{i}"/> </h:inputText> <s:selectDate for="answerInput#{i}"> <h:graphicImage url="img/dtpick.gif" style="margin-left:5px"/> </s:selectDate> </crs:isDate> <crs:isBoolean> <h:selectBooleanCheckbox id="answerInput#{i}" value="#{registrationProcess.pageAnswers[ i].valueBoolean}" required="${required}"/> </crs:isBoolean> <br clear="none"/> <a:outputPanel id="questionErrors#{i}"> <s:message/> </a:outputPanel> </s:decorate> </h:panelGroup> </crs:customQuestions> </h:panelGrid> </s:validateAll>
And the main taghandler (which I modeled after facelets' ForEachHandler) looks like this:
public class CustomQuestionTagHandler extends TagHandler { private final TagAttribute registrationType; private final TagAttribute questionTextVar; private final TagAttribute questionNumberVar; private final TagAttribute requiredVar; public CustomQuestionTagHandler(TagConfig config) { super(config); registrationType = getRequiredAttribute("registrationType"); questionTextVar = getRequiredAttribute("questionTextVar"); questionNumberVar = getRequiredAttribute("questionNumberVar"); requiredVar = getRequiredAttribute("requiredVar"); } @SuppressWarnings("unchecked") public void apply(FaceletContext faceletContext, UIComponent parent) throws IOException, FacesException, FaceletException, ELException { RegistrationType registrationType = (RegistrationType) this.registrationType.getObject(faceletContext, RegistrationType.class); assert registrationType != null; List<QuestionUsage> questionUsages = registrationType.getQuestionUsages(); assert questionUsages != null; String questionTextVar = this.questionTextVar.getValue(faceletContext); String questionNumberVar = this.questionNumberVar.getValue(faceletContext); String requiredVar = this.requiredVar.getValue(faceletContext); VariableMapper variables = faceletContext.getVariableMapper(); int i = 1; for (QuestionUsage questionUsage : questionUsages) { Question question = questionUsage.getQuestion(); Integer questionNumber = new Integer(i); ValueExpression questionNumberValueExpression = faceletContext.getExpressionFactory() .createValueExpression(questionNumber, Integer.class); variables.setVariable(questionNumberVar, questionNumberValueExpression); ValueExpression questionTextValueExpression = faceletContext.getExpressionFactory().createValueExpression( question.getText(), String.class); variables.setVariable(questionTextVar, questionTextValueExpression); Boolean required = questionUsage.isRequired(); variables.setVariable(requiredVar, faceletContext.getExpressionFactory().createValueExpression(required, Boolean.class)); faceletContext.setAttribute("questionType", question.getType()); // execute body this.nextHandler.apply(faceletContext, parent); i++; } } }
This renders fine. But during the onblur ajax validation check, this occurs:
java.lang.NullPointerException at org.jboss.seam.core.Expressions.getValidator(Expressions.java:198) at org.jboss.seam.core.Expressions.validate(Expressions.java:182) at org.jboss.seam.ui.ModelValidator.validate(ModelValidator.java:25) at javax.faces.component._ComponentUtils.callValidators(_ComponentUtils.java:157) at javax.faces.component.UIInput.validateValue(UIInput.java:312) at javax.faces.component.UIInput.validate(UIInput.java:353) at javax.faces.component.UIInput.processValidators(UIInput.java:183) at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:624) at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:624) at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:624) at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:624) at javax.faces.component.UIForm.processValidators(UIForm.java:70) at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:624) at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:146) at org.ajax4jsf.framework.ajax.AjaxViewRoot.processValidators(AjaxViewRoot.java:389) ...
It appears that Expressions.validate(...) is trying to evaluate #{registrationProcess.pageAnswers[ i].valueString}, which returns null.
I don't know enough about EL to know why. I suspect either I can't use model validation this way, or I need to do something special in my TagHandler. Does anyone have any recommendations?
Also, a null-check should probably be put in Expressions.java (line 198ish) before calling getValidator().
Thanks!