I need to implement forms with a "save-your-work" as well as a "submit" button. The user needs to be able to save their work before it is complete. This causes problems with the validation of required values. I have looked at the solution suggested here JSF 2.0: How to skip JSR-303 bean validation? and others like it. They require adding a parameter to the button that requires the validation
<h:commandButton>
<f:param name="SHOW_VALIDATION" value="true" />
</h:commandButton>
And then festooning every input tag with a test
<h:inputText>
<f:validateRequired disabled="#{!param['VALIDATE']}" />
</h:inputText>
A cleaner and more secure solution is outlined in http://javalabor.blogspot.com/2012/02/jsf-2-conditional-validation.html. This approach was also attempted here https://community.jboss.org/thread/211982?tstart=0&_sscc=t. Cleaner because only one change is needed in the form to indicate which button should skip validation using the button's id. More secure because one is not adding a parameter to the request that is potentially manipulated and whose purpose is to bypass validation! The goal is to replace the base validator so the custom validator can detect which button is clicked and skip validation on the entire page.
The faces-config.xml is
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<application>
<default-validators>
<validator-id>javax.faces.Bean</validator-id>
</default-validators>
<default-validators>
<validator-id>javax.faces.Required</validator-id>
</default-validators>
</application>
<validator>
<validator-id>javax.faces.Bean</validator-id>
<validator-class>edu.wustl.mir.ctt.util.SkipBeanValidator</validator-class>
</validator>
<validator>
<validator-id>javax.faces.Required</validator-id>
<validator-class>edu.wustl.mir.ctt.util.SkipRequiredValidator</validator-class>
</validator>
The custom validators are
public class SkipRequiredValidator extends RequiredValidator {
@Override
public void validate(final FacesContext context, final UIComponent component, final Object value) {
if (ValidatorUtil.check(context)) {
super.validate(context, component, value);
}
}
}
public class SkipBeanValidator extends BeanValidator {
@Override
public void validate(final FacesContext context, final UIComponent component, final Object value) {
if (ValidatorUtil.check(context)) {
super.validate(context, component, value);
}
}
}
And the helper class
public class ValidatorUtil {
private static final String VALIDATE = "VALIDATE";
public static boolean check() {
return check(FacesContext.getCurrentInstance());
}
public static boolean check(final FacesContext context) {
final ExternalContext externalContext = context.getExternalContext();
final Object validate = externalContext.getRequestMap().get(VALIDATE);
if (validate != null) {
return (Boolean) validate;
}
for (final Map.Entry<String, String[]> requestParameters : externalContext.getRequestParameterValuesMap()
.entrySet()) {
final String key = requestParameters.getKey();
if (key.contains(":do")) {
externalContext.getRequestMap().put(VALIDATE, Boolean.TRUE);
return true;
}
}
externalContext.getRequestMap().put(VALIDATE, Boolean.FALSE);
return false;
}
}
Using tomcat7 and mojarra2.2, I can see that custom Required validator is indeed replaced and super.validate() is NOT called as appropriate to the detected button id but the form is still validated for required fields.
Can anyone see what is wrong with what otherwise seems like an elegant solution?
Thanks in advance.