The following is a simple use case of .
This is specified behavior. When PROCESS_VALIDATIONS phase ends with a validation failure, both the UPDATE_MODEL_VALUES and INVOKE_APPLICATION phases are skipped. Exactly like as in "regular" forms with . Think of as a and a as a and it will become more clear.
For your particular requirement, performing a redirect when conversion/validation has failed, there are at least 3 solutions:
As you found out, add a . I'd rather hook on postValidate event instead for better self-documentability.
public void redirectIfNecessary() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (!context.isPostback() && context.isValidationFailed()) {
context.getExternalContext().redirect("some.xhtml");
}
}
The check on FacesContext#isPostback() prevents the redirect being performed on validation failures of "regular" forms in the same view (if any).
Extend the builtin LongConverter whereby you perform the redirect in getAsObject() (a validator is insuitable as the default converter for Long would already fail on non-numeric inputs; if a converter fails, the validators are never fired). This is however poor design (tight-coupling).
@FacesConverter("idConverter")
public class IdConverter extends LongConverter {
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || !value.matches("[0-9]{1,20}")) {
try {
context.getExternalContext().redirect("some.xhtml");
return null;
}
catch (IOException e) {
throw new FacesException(e);
}
}
else {
return super.getAsObject(context, component, value);
}
}
}
You could if necessary play around with inside to "pass" parameters to the converter.
String redirect = (String) component.getAttributes().get("redirect");
context.getExternalContext().redirect(redirect);
Create a custom taghandler which does basically the same as but without the need for an additional backing bean method.
com.example.taghandler.ViewParamValidationFailed
public class ViewParamValidationFailed extends TagHandler implements ComponentSystemEventListener {
private String redirect;
public ViewParamValidationFailed(TagConfig config) {
super(config);
redirect = getRequiredAttribute("redirect").getValue();
}
@Override
public void apply(FaceletContext context, UIComponent parent) throws IOException {
if (parent instanceof UIViewRoot && !context.getFacesContext().isPostback()) {
((UIViewRoot) parent).subscribeToEvent(PostValidateEvent.class, this);
}
}
@Override
public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.isValidationFailed()) {
try {
context.getExternalContext().redirect(redirect);
}
catch (IOException e) {
throw new AbortProcessingException(e);
}
}
}
}
/WEB-INF/my.taglib.xml
http://example.com/ui
viewParamValidationFailed
com.example.taghandler.ViewParamValidationFailed
/WEB-INF/web.xml
javax.faces.FACELETS_LIBRARIES
/WEB-INF/my.taglib.xml
True, it's a bit of code, but it ends up in clean and reusable tag and is actually a good fit for a new OmniFaces feature.