This is indeed as per the specification. Here's an extract of relevance from UIInput#validate()
javadoc (emphasis mine):
Retrieve the submitted value with getSubmittedValue()
. If this returns null
, and the value of the ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE
context-param is true (ignoring case), examine the value of the "required" property. If the value of "required" is true, continue as below. If the value of "required" is false or the required attribute is not set, exit without further processing. If the context-param is not set, or is set to false (ignoring case), exit without further processing. (This indicates that no value was submitted for this component.)
An empty input will send an empty string, not null
. A complete absence of the input will send null
, not empty string.
You can thus disable the observed behavior by adding the following context parameter:
<context-param>
<param-name>javax.faces.ALWAYS_PERFORM_VALIDATION_WHEN_REQUIRED_IS_TRUE</param-name>
<param-value>true</param-value>
</context-param>
Note that this context parameter is new since JSF 2.3 and backported into Mojarra 2.2.16, 2.1.29-10 and 1.2_15-06. It is not supported in older versions. See also JSFSPEC-1433 and the expert group discussion about this issue.
Whether that is harmful or not depends on the business logic. A decently designed model (business logic and/or data model) which doesn't consider null
as expected case would cause a null pointer exception elsewhere, or a SQL constraint violation (NOT NULL
), which will usually end up in a HTTP 500 error response. But if the model actually considers null
as an expected case, then it's likely a fault in the model. The view (the JSF page), intented to merely present the model, can then do little against it.
If the business logic or data model can really not be altered to consider null
as an exceptional case (i.e. never assume/accept the given value as null
), and you happen to use JPA, then your best bet is to add a @NotNull
on the property. Whilst JSF will bypass validation on it, JPA will still validate it, causing still an exception and a HTTP 500 error. I'd in this case only wonder why the DB column doesn't have a NOT NULL
constraint in first place. Alternatively, do class level validation.
Noted should be that MyFaces logs a warning like below on this:
Mar 16, 2016 8:55:52 AM org.apache.myfaces.shared.renderkit.html.HtmlRendererUtils decodeUIInput
WARNING: There should always be a submitted value for an input if it is rendered, its form is submitted, and it was not originally rendered disabled or read-only. You cannot submit a form after disabling an input element via javascript. Consider setting read-only to true instead or resetting the disabled value back to false prior to form submission.
Component : {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /test.xhtml][Class: javax.faces.component.html.HtmlBody,Id: j_id_5][Class: javax.faces.component.html.HtmlForm,Id: j_id_6][Class: javax.faces.component.html.HtmlInputText,Id: j_id_7] Location: /test.xhtml at line 22 and column 33}