I have just migrated to Tomcat 8. I used to work with system property org.apache.el.parser.COERCE_TO_ZERO=false
so empty strings, numbers, booleans etc. are treated as null
.
In Tomcat 8, EL 3.0, it is supposed to be the default but it is in fact converting null
string to empty string ""
on JSF side.
It is supposed to be a bug and it is supposed to be corrected but I'm not able to get it working in TomEE snapshot (Tomcat 8.0.27.0, MyFaces 2.2.8).
This is a bug in the EL 3.0 specification. Since EL 3.0, as consequence of overzealous fix of JSP spec issue 184, null
will be coerced back to empty string before the model value setter is invoked. Basically, the javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL
doesn't have any affect anymore. See also JSF issue 3071 and EL spec issue 18.
Basically, to solve this problem you need to provide either a custom EL resolver, or to replace/upgrade the EL implementation (or simply the whole server as it's the one actually providing EL out the box) to a version with the bugfix. The Tomcat issue 57309 which you linked is not related to this particular EL 3.0 issue of unnecessary coercion from null to empty string, it's only related to Tomcat's ignorance of the custom EL resolver.
You can solve this problem in two ways:
Provide a custom EL resolver like below. Make sure that you use Tomcat 8.0.16 or newer, as specified in the Tomcat issue report you found.
public class EmptyToNullStringELResolver extends ELResolver {
@Override
public Class<?> getCommonPropertyType(ELContext context, Object base) {
return String.class;
}
@Override
public Object convertToType(ELContext context, Object value, Class<?> targetType) {
if (value == null && targetType == String.class) {
context.setPropertyResolved(true);
}
return value;
}
@Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
return null;
}
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
return null;
}
@Override
public Object getValue(ELContext context, Object base, Object property) {
return null;
}
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
return true;
}
@Override
public void setValue(ELContext context, Object base, Object property, Object value) {
// NOOP.
}
}
In order to get it to run, register as below in faces-config.xml
:
<application>
<el-resolver>com.example.EmptyToNullStringELResolver</el-resolver>
</application>
Or, switch to Oracle EL. They already fixed it in version 3.0.1 b05 which has been available since 7 July 2014 (just pick the newest one which is 3.0.1-b10 as of today).
Just drop javax.el.jar
file in /WEB-INF/lib
and add the below context param to web.xml
in order to instruct MyFaces to use the Oracle EL implementation instead:
<context-param>
<param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
<param-value>com.sun.el.ExpressionFactoryImpl</param-value>
</context-param>
In case you're using Mojarra, use param name com.sun.faces.expressionFactory
.
As I got momentarily confused myself too, I blogged to get it all right: The empty String madness.
See also:
- javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL does not work anymore since Java EE 7 / EL 3.0