3 Replies Latest reply on Feb 23, 2007 10:43 PM by Matt Drees

    NPE in Expressions.java doing model validation

    Matt Drees Master

      My 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!