This discussion is archived
9 Replies Latest reply: Nov 1, 2012 4:59 AM by 905019 RSS

Skipping validation phase to allow model update / invoke application phase

jmsjr Newbie
Currently Being Moderated
JSF2

I have a need to skip the PROCESS_VALIDATIONS lifecycle phase, but still proceed to the UPDATE_MODEL_VALUES and INVOKE_APPLICATION phases.
Now before someone says to use immediate="true", that will not work for the requirement that I have as that will go straight to the RENDER_RESPONSE phase.

It is basically a Cancel button, but the cancel button itself needs to invoke a method in the backing bean ( e.g. unlock an external resource ), and then close the window. The external resource to unlock is specified as HTML input hidden parameters. Thus, the need to have the UPDATE_MODEL_VALUES, as I need to know the parameters to the external resource to unlock, and the need to have the INVOKE_APPLICATION phase, as I need to invoke a method on the bean to actually unlock the external resource.

Now I am using the example provided here:

http://javalabor.blogspot.com.au/2012/02/jsf-2-conditional-validation.html

So along the same lines, I have:

1) The following in my faces-config.xml file:
 <validator>
  <validator-id>javax.faces.Required</validator-id>
  <validator-class>xx.yy.zz.SkipRequiredValidator</validator-class>
 </validator>
 <validator>
  <validator-id>javax.faces.Bean</validator-id>
  <validator-class>xx.yy.zz.SkipBeanValidator</validator-class>
 </validator>
2) The following class as the SkipRequiredValidator
package xx.yy.zz;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.RequiredValidator;

/**
 * This validator will skip the JSF validation phase if the Submit action 
 * has the parameter cancel with a value of "Cancel".
 * 
 * This is required as we still want to have the Invoke Application JSF lifecycle
 * executed in order to have the workItem unlocked.
 * 
 * @author salvojo
 */
public class SkipRequiredValidator extends RequiredValidator {
     
     private static Logger logger = Logger.getLogger(SkipRequiredValidator.class.getName());
     
     @Override
     public void validate(final FacesContext facesContext, final UIComponent component, final Object value) {
          
          if( SkipValidatorUtil.continueValidation(facesContext)) {
               logger.log( Level.FINER, "Calling standard validation" );
               super.validate(facesContext, component, value);
          } else {
               logger.log( Level.FINER, "Skipping standard validation" );
          }
     }     

}
3) The following class as the SkipBeanValidator:
package xx.yy.zz

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.BeanValidator;

/**
 * This validator will skip the JSF validation phase if the Submit action 
 * has the parameter cancel with a value of "Cancel".
 * 
 * This is required as we still want to have the Invoke Application JSF lifecycle
 * executed in order to have the workItem unlocked.
 * 
 * @author salvojo
 */

public class SkipBeanValidator extends BeanValidator {
     
     private static Logger logger = Logger.getLogger(SkipBeanValidator.class.getName());
     
     @Override
     public void validate(final FacesContext facesContext, final UIComponent component, final Object value) {
          
          if( SkipValidatorUtil.continueValidation(facesContext)) {
               logger.log( Level.FINER, "Calling standard validation" );
               super.validate(facesContext, component, value);
          } else {
               logger.log( Level.FINER, "Skipping standard validation" );
          }
     }          

}
4) And the utility class
package xx.yy.zz;

import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.context.FacesContext;

/**
 * Utility method that will:
 * 
 * 1) Check for a request parameter of attribute {@link SkipValidatorUtil#VALIDATE}
 * 
 * 2) If found, return the value of that attribute. It is assumed to be a Boolean.
 * 
 * 3) If not found, checks for a parameter with name that ends in ":cancel" ( the 
 * text before the colon is the id of the form, which could change ). If found,
 * checks that the value of this parameter is "Cancel". If so, sets the
 * request parameter of attribute {@link SkipValidatorUtil#VALIDATE} to false.
 * If not found, or found but the value of the parameter is not "Cancel",
 * then sets the request parameter of attribute {@link SkipValidatorUtil#VALIDATE} to true.
 * 
 * @author salvojo
 */
public final class SkipValidatorUtil {
     
     private static final String VALIDATE = "xx.yy.zz.VALIDATE";
     
     private static Logger logger = Logger.getLogger(SkipValidatorUtil.class.getName());
     
     public static boolean continueValidation(final FacesContext facesContext) {
          
          Boolean continueValidation = (Boolean) facesContext
               .getExternalContext()
               .getRequestMap()
               .get(SkipValidatorUtil.VALIDATE);
          
          if( continueValidation == null ) {
               continueValidation = true;
               
               for( Entry<String,String> requestParameter : facesContext
                    .getExternalContext()
                    .getRequestParameterMap().entrySet()) {
                    
                    if( requestParameter.getKey().contains(":cancel") &&
                         "Cancel".equals(requestParameter.getValue()) ) {
                         continueValidation = false;
                         
                         break;
                    }
               }
               
               facesContext
                    .getExternalContext()
                    .getRequestMap()
                    .put(SkipValidatorUtil.VALIDATE, continueValidation);
          }
          
          logger.log( Level.FINER, "Validation will be continued? " + continueValidation );
          
          return continueValidation;
     }

}
5) And in my XHTML page, the following snippet:
<h:inputText id="f_mbrId" value="#{insuranceCase.planMemberId}" required="true">
<h:inputHidden id="workItemTag" value="#{insuranceCase.workItemTag}"/>

<rich:messages/>
<h:commandButton id="cancel" value="Cancel" action="#{insuranceCase.unlock}"></h:commandButton>
6) After deploying, when I click on the Cancel button, I am STILL getting a validation messages rendered when I leave the f_mbrId HTML input field blank, and the action on the backing bean is never invoked. The following output on the log file shows what happens:
18:41:46,155 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2) Before Phase: RESTORE_VIEW 1 Session exist: true
18:41:46,156 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2)     Session: com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap => {2765221448941953821={-5650150279934172176=[Ljava.lang.Object;@37a9aa}, 4523587801893665884={-84153624240172225=[Ljava.lang.Object;@1a46cd2}}
18:41:46,159 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2)     Session: javax.faces.request.charset => UTF-8
18:41:46,167 FINER [xx.yy.zz.ActionProcessorListener] (http--0.0.0.0-8081-2)         Request: j_idt4:f_mbrId =>
18:41:46,184 FINER [xx.yy.zz.ActionProcessorListener] (http--0.0.0.0-8081-2)         Request: j_idt4:workItemTag => aasdev1|JSFTESTP|salvojo|R|29628|8603194|aasdev1|STRTCASE|0|9
18:41:46,189 FINER [xx.yy.zz.ActionProcessorListener] (http--0.0.0.0-8081-2)         Request: j_idt4:cancel => Cancel
18:41:46,191 FINER [xx.yy.zz.ActionProcessorListener] (http--0.0.0.0-8081-2)         Request: javax.faces.ViewState => 4523587801893665884:-84153624240172225
18:41:46,192 FINER [xx.yy.zz.ActionProcessorListener] (http--0.0.0.0-8081-2) Not an initial request to start or open an existing workitem
18:41:46,195 FINER [xx.yy.zz.ActionProcessorListener] (http--0.0.0.0-8081-2) After Phase: RESTORE_VIEW 1 Session exist: true Request Path: null
18:41:46,196 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2) After Phase: RESTORE_VIEW 1 Session exist: true Request Path: null
18:41:46,197 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2) Before Phase: APPLY_REQUEST_VALUES 2 Session exist: true
18:41:46,197 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2)     Session: com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap => {2765221448941953821={-5650150279934172176=[Ljava.lang.Object;@37a9aa}, 4523587801893665884={-84153624240172225=[Ljava.lang.Object;@1a46cd2}}
18:41:46,199 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2)     Session: javax.faces.request.charset => UTF-8
18:41:46,200 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2) After Phase: APPLY_REQUEST_VALUES 2 Session exist: true Request Path: null
18:41:46,201 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2) Before Phase: PROCESS_VALIDATIONS 3 Session exist: true
18:41:46,201 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2)     Session: com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap => {2765221448941953821={-5650150279934172176=[Ljava.lang.Object;@37a9aa}, 4523587801893665884={-84153624240172225=[Ljava.lang.Object;@1a46cd2}}
18:41:46,203 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2)     Session: javax.faces.request.charset => UTF-8
18:41:46,203 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,204 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,205 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,205 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,207 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,208 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,208 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,209 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,210 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,210 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,211 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,212 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,213 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,213 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,214 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,215 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,215 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,216 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,217 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,217 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,218 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,219 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,219 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,220 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,221 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,221 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,222 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,223 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,223 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,224 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,225 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,225 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,226 FINER [xx.yy.zz.SkipValidatorUtil] (http--0.0.0.0-8081-2) Validation will be continued? false
18:41:46,226 FINER [xx.yy.zz.SkipBeanValidator] (http--0.0.0.0-8081-2) Skipping standard validation
18:41:46,227 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2) After Phase: PROCESS_VALIDATIONS 3 Session exist: true Request Path: null
18:41:46,228 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2) Before Phase: RENDER_RESPONSE 6 Session exist: true
18:41:46,229 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2)     Session: com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap => {2765221448941953821={-5650150279934172176=[Ljava.lang.Object;@37a9aa}, 4523587801893665884={-84153624240172225=[Ljava.lang.Object;@1a46cd2}}
18:41:46,230 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2)     Session: javax.faces.request.charset => UTF-8
18:41:46,239 FINER [xx.yy.zz.jsf.PhaseTracker] (http--0.0.0.0-8081-2) After Phase: RENDER_RESPONSE 6 Session exist: true Request Path: null
As you can see from the above, it did not go through the UPDATE_MODEL_VALUES phase and did not go through the INVOKE_APPLICATION phase. It went stragith through to RENDER_RESPONSE phase, as if ignoring the validators that I provided.

So my question are:

*) Does providing your own validators in faces-config.xml override the default validators provided by the JSF implementation ?
*) If yes, then what I am doing wrong above .. as I only call the superclass validate() method when I want to ?
*) If no, what is an alternative ?

Edited by: jmsjr on 26-Oct-2012 01:12

Edited by: jmsjr on 26-Oct-2012 01:30
  • 1. Re: Skipping validation phase to allow model update / invoke application phase
    gimbal2 Guru
    Currently Being Moderated
    Yeah I'd start by reposting all your code between \
     tags to actually make it readable. Nobody is going to go through a wall of unformatted code.                                                                                                                                                                                                                                                                                                            
  • 2. Re: Skipping validation phase to allow model update / invoke application phase
    jmsjr Newbie
    Currently Being Moderated
    gimbal2 wrote:
    Yeah I'd start by reposting all your code between \
     tags to actually make it readable. Nobody is going to go through a wall of unformatted code.
    Sorry ... done now.
  • 3. Re: Skipping validation phase to allow model update / invoke application phase
    jmsjr Newbie
    Currently Being Moderated
    Come to think of it, maybe the solution that I was copying from
    jmsjr wrote:

    http://javalabor.blogspot.com.au/2012/02/jsf-2-conditional-validation.html

    ... was too complicated. I think a simpler solution would be to change the action to:

    1) Add the immediate="true" attribute ... again
    2) Change the action to point to a different page ( Maybe even to a standard servlet . Can you forward to a non JSF page via the action attribute ? )
    <h:inputHidden id="workItemTag" value="#{insuranceCase.workItemTag}"/>
    <h:commandButton id="cancel" immediate="true" value="Cancel" action="unlockWorkItem"></h:commandButton>
    3) It is assumed that the forwarded page have access to the request parameter ( e.g. the workItemTag value above is available in the RequestParameter )
  • 4. Re: Skipping validation phase to allow model update / invoke application phase
    gimbal2 Guru
    Currently Being Moderated
    I'm not that clever, but so much effort just to not have validations done... why bother I wonder. I'd say in this particular case dump all validations on the JSF side of things (so don't use required, don't configure any JSF validators) and simply validate stuff upon the actual action button press you will eventually invoke, the good old fashioned manual way.
    public String doSave(){
      if(somecondition == false){
        //add JSF message informing what needs to be done
        return null;
      }
    
      someBean.saveThingsAndStuff(thing);
    }
  • 5. Re: Skipping validation phase to allow model update / invoke application phase
    nickarls Newbie
    Currently Being Moderated
    I've had a case where I ended up doing the validation with BeanValidation manually in the backing bean. Of course then you have to manually target the components with the messages which can be a PITA if they are many.
  • 6. Re: Skipping validation phase to allow model update / invoke application phase
    gimbal2 Guru
    Currently Being Moderated
    nickarls wrote:
    I've had a case where I ended up doing the validation with BeanValidation manually in the backing bean. Of course then you have to manually target the components with the messages which can be a PITA if they are many.
    Yeah but programming is hard :) Being able to do it the easy way is more the exception than the rule.
  • 7. Re: Skipping validation phase to allow model update / invoke application phase
    jmsjr Newbie
    Currently Being Moderated
    jmsjr wrote:
    Come to think of it, maybe the solution that I was copying from
    jmsjr wrote:

    http://javalabor.blogspot.com.au/2012/02/jsf-2-conditional-validation.html

    ... was too complicated. I think a simpler solution would be to change the action to:

    1) Add the immediate="true" attribute ... again
    2) Change the action to point to a different page ( Maybe even to a standard servlet . Can you forward to a non JSF page via the action attribute ? )
    <h:inputHidden id="workItemTag" value="#{insuranceCase.workItemTag}"/>
    <h:commandButton id="cancel" immediate="true" value="Cancel" action="unlockWorkItem"></h:commandButton>
    3) It is assumed that the forwarded page have access to the request parameter ( e.g. the workItemTag value above is available in the RequestParameter )
    The above simple solution works for the 'Cancel' button so that I skip all the validation as follows:

    1) Use immediate="true" on the Cancel button
    2) Add a PhaseListener that listens on the RESTORE_VIEW phase. This phase listener then checks the requests attributes / parameters to check if the "Cancel" button was selected. If so, calls something else to unlock the workItem.
    3) The immediate="true" then forces the VALIDATION phase to be skipped
    4) The action attribute on the button forwards to a page that simply closes the page

    While the above works for both RequestScoped and ViewScoped managed beans, I have a separate but related issue with regards to validations:

    A) When using ViewScoped managed beans for the values displayed on the HTML form, if any of the properties of the bean are required ( via the @NotNull annotation on the property of the managed bean ), and then a postback of the view is done, and the validation fails ... when the view is rendered again, you see the PREVIOUS values that were entered. To explain further:

    A1) Say back account number is required and is backed by a ViewScoped managed bean.
    A2) We have the following:
    <h:inputText id="bankAccountNumber" value="#{bankAccount.accountNumber}"/>
    A3) On initial load of the view / JSF page, the bank account number is 123.
    A4) User then clears the back account number or enters a bank account number that is invalid
    A5) User submits the form
    A6) JSF renders response. User sees message "Bank account number is required" or "is invalid".
    A7) But the rendered response still shows the back account 123, instead of the blank or invalid bank account number. This is because the JSF lifecycle skips the UPDATE_MODEL_VALUES phase as the validation has failed. Now the message being shown to the user ( "required or invalid" ) does not match what is now shown on the form, as what is shown on the form is the previously valid value. From a user point of view, they would expect the form to show what they have entered ( the blank or invalid bank account number ).

    A workaround for the above seems to be use RequestScoped managed bean. On [A7] above, the rendered value for the bank account number is the invalid value that was entered by the user, which is the desired behaviour ... BUT ...

    The managed bean has a @PostConstruct method to perform some initialisation, connect to an external resource, etc... I was using @ViewScoped so that the initialisation only happens once per view, not per request.

    What I now need is:

    * To be able to use @ViewScoped managed beans ( to limit the calls to @PostConstruct )
    * When validation fails, the rendered response phase to show the invalid values that were entered.

    It seems that the JSF lifecycle is not really meant for this kind of scenario.
  • 8. Re: Skipping validation phase to allow model update / invoke application phase
    jmsjr Newbie
    Currently Being Moderated
    gimbal2 wrote:
    I'm not that clever, but so much effort just to not have validations done... why bother I wonder. I'd say in this particular case dump all validations on the JSF side of things (so don't use required, don't configure any JSF validators) and simply validate stuff upon the actual action button press you will eventually invoke, the good old fashioned manual way.
    public String doSave(){
    if(somecondition == false){
    //add JSF message informing what needs to be done
    return null;
    }
    
    someBean.saveThingsAndStuff(thing);
    }
    Using your suggestion:

    1) How do I "don't configure any JSF validators" ?

    2) I still want to use Hibernate validation via the annotations provided, but I would like to do call the Hibernate validation myself without having JSF do that for me. Is there something in the framework to remove validations ?

    3) If I do the validation myself in the INVOKE_APPLICATION phase of the JSF lifecycle, will I be able to add messages that will be displayed by <h:messages> or <rich:messages> ?

    4) What about <h:message> ? Since the validation would be done in the INVOKE_APPLICATION lifecycle, how would I know in the manage beans's method what UI component to attach the message to ?

    Edited by: jmsjr on 31-Oct-2012 20:30
  • 9. Re: Skipping validation phase to allow model update / invoke application phase
    905019 Newbie
    Currently Being Moderated
    Use a specific validation group. You can add messages through FacesContext but you either have to know their id:s (you set them yourself) or you bind the components (or do some other sort of trick in the pre-render-response phase)

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points