6 Replies Latest reply on Jan 30, 2008 7:46 AM by Pete Muir

    NPE in Param.validateConvertedValue

    Joshua Davis Expert

      I've been getting null pointer exceptions with Seam 2.0.0.GA with a .page.xml file / application that used to work fine in 1.2.1.GA. The error happens when processing the restful parameters:

      2008-01-23 14:44:57,532 [http-0.0.0.0-8080-1] ERROR SeamPhaseListener - uncaught exception
      java.lang.NullPointerException
       at org.jboss.seam.navigation.Param.validateConvertedValue(Param.java:246)
       at org.jboss.seam.navigation.Pages.convertAndValidateStringValuesInPageContext(Pages.java:743)
       at org.jboss.seam.navigation.Pages.postRestore(Pages.java:393)
      


      This is the code in Param.java that fails:
       if (valueExpression!=null)
       {
       //TODO: note that this code is duplicated from ModelValidator!!
       ELContext elContext = facesContext.getELContext();
       InvalidValue[] invalidValues;
       try
       {
       invalidValues = Validators.instance().validate( valueExpression.toUnifiedValueExpression(), elContext, value );
       }
       catch (ELException ele)
       {
       Throwable cause = ele.getCause();
       if (cause==null) cause = ele;
       throw new ValidatorException( createMessage(cause), cause );
       }
      
       if ( invalidValues.length>0 ) // <== Boom! NPE
       {
       throw new ValidatorException( createMessage(invalidValues) );
       }
       }
      


      I took a look at the code and saw that there is an array of 'invalid values' which is expected to be of zero length if there are no errors but in this case it's null. The strange thing is that there are other places in the application where restful parameters work just fine. The only thing I can think of is that in this case, the restful parameter is being used inside a facelets template (the parameter turns the top nav bar off).

      Anyone see this before?
      I suppose I could fix it by overriding the definition of Validators with my own, or perhaps patching Param.



        • 1. Re: NPE in Param.validateConvertedValue
          Joshua Davis Expert

          Okay, I think I've figured out why this NPE only happens sometimes. The .page.xml file in this case binds a simple expression to the parameter value. For example:

           <param name="windowType" value="#{windowType}"/>
          


          This value is intended to be passed down through the Facelets templates and turn off nav bars, etc. With this kind of expression, ValidatingResolver.getInvalidValues() returns null because of the condition in the if statement:
           @Override
           public void setValue(ELContext context, Object base, Object property, Object value)
           throws NullPointerException, PropertyNotFoundException, PropertyNotWritableException, ELException
           {
           if (base!=null && property!=null ) // <== This is false, so invalidValues is never set.
           {
           context.setPropertyResolved(true);
           invalidValues = getValidator(base).getPotentialInvalidValues( property.toString(), value );
          
           }
          


          • 2. Re: NPE in Param.validateConvertedValue
            Joshua Davis Expert

            I was able to fix this by initializing the 'invalidValues' field to a zero length array inside Validators.ValidatingResolver's constructor. I added it to my application, overriding the existing Seam component:

            package com.foo.seam;
            
            import org.hibernate.validator.ClassValidator;
            import org.hibernate.validator.InvalidValue;
            import org.jboss.seam.Component;
            import org.jboss.seam.Instance;
            import org.jboss.seam.ScopeType;
            import org.jboss.seam.core.SeamResourceBundle;
            import org.jboss.seam.annotations.Name;
            import org.jboss.seam.annotations.Scope;
            import org.jboss.seam.annotations.intercept.BypassInterceptors;
            import org.jboss.seam.contexts.Contexts;
            import org.jboss.seam.el.EL;
            
            import javax.el.ELContext;
            import javax.el.ELException;
            import javax.el.ELResolver;
            import javax.el.PropertyNotFoundException;
            import javax.el.PropertyNotWritableException;
            import javax.el.ValueExpression;
            import java.beans.FeatureDescriptor;
            import java.util.Iterator;
            import java.util.Locale;
            import java.util.Map;
            import java.util.concurrent.ConcurrentHashMap;
            
            /**
             * Caches instances of Hibernate Validator ClassValidator
             *
             * @author Gavin King
             * @author Josh - patched up the inner class.
             */
            @Name("org.jboss.seam.core.validators")
            @BypassInterceptors
            @Scope(ScopeType.APPLICATION)
            public class Validators extends org.jboss.seam.core.Validators
            {
            
             /**
             * Validate that the given value can be assigned to the property given by the value
             * expression.
             *
             * @param valueExpression a value expression, referring to a property
             * @param elContext the ELContext in which to evaluate the expression
             * @param value a value to be assigned to the property
             * @return a set of potential InvalidValues, from Hibernate Validator
             */
             public InvalidValue[] validate(ValueExpression valueExpression, ELContext elContext, Object value)
             {
             ValidatingResolver validatingResolver = new ValidatingResolver( elContext.getELResolver() );
             ELContext decoratedContext = EL.createELContext(elContext, validatingResolver);
             valueExpression.setValue(decoratedContext, value);
             return validatingResolver.getInvalidValues();
             }
            
             class ValidatingResolver extends ELResolver
             {
             private ELResolver delegate;
             private InvalidValue[] invalidValues;
            
             public ValidatingResolver(ELResolver delegate)
             {
             this.delegate = delegate;
             invalidValues = new InvalidValue[0]; // <== Initialize array.
             }
            
             public InvalidValue[] getInvalidValues()
             {
             return invalidValues;
             }
            
             @Override
             public Class<?> getCommonPropertyType(ELContext context, Object value)
             {
             return delegate.getCommonPropertyType(context, value);
             }
            
             @Override
             public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object value)
             {
             return delegate.getFeatureDescriptors(context, value);
             }
            
             @Override
             public Class<?> getType(ELContext context, Object x, Object y)
             throws NullPointerException, PropertyNotFoundException, ELException
             {
             return delegate.getType(context, x, y);
             }
            
             @Override
             public Object getValue(ELContext context, Object base, Object property)
             throws NullPointerException, PropertyNotFoundException, ELException
             {
             return delegate.getValue(context, base, property);
             }
            
             @Override
             public boolean isReadOnly(ELContext context, Object base, Object property)
             throws NullPointerException, PropertyNotFoundException, ELException
             {
             return delegate.isReadOnly(context, base, property);
             }
            
             @Override
             public void setValue(ELContext context, Object base, Object property, Object value)
             throws NullPointerException, PropertyNotFoundException, PropertyNotWritableException, ELException
             {
             if (base!=null && property!=null )
             {
             context.setPropertyResolved(true);
             invalidValues = getValidator(base).getPotentialInvalidValues( property.toString(), value );
             }
             }
             }
            }
            



            • 3. Re: NPE in Param.validateConvertedValue
              Joshua Davis Expert

              Should I file this as a bug?

              • 4. Re: NPE in Param.validateConvertedValue
                Pete Muir Master

                 

                <param name="windowType" value="#{windowType}"/>


                isn't really valid in pages.xml's params - it needs to be #{window.type} - resolvable in both directions.

                But file a bug, an NPE there isn't nice!

                • 5. Re: NPE in Param.validateConvertedValue
                  Joshua Davis Expert

                   

                  "pete.muir@jboss.org" wrote:
                  <param name="windowType" value="#{windowType}"/>


                  isn't really valid in pages.xml's params - it needs to be #{window.type} - resolvable in both directions.

                  But file a bug, an NPE there isn't nice!


                  Thanks I'll file it. BTW, I found the JS memory leak in seam remoting on IE6 again.

                  I'll also try #{window.type}, window should be an event scoped bean then?