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!