Tomcat 8 (and 9) coerce behaviour, null strings ar

2019-01-19 03:17发布

问题:

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).

回答1:

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:

  1. 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>
    

  2. 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