ValidateBean method forEachInputWithMatchingBase throws NullPointerException for dynamic property with base as map object #227

Closed
vijpan opened this Issue Mar 30, 2016 · 8 comments

Projects

None yet

2 participants

@vijpan
vijpan commented Mar 30, 2016

Similar to use case as per the issue #225 - in tag handler ValidateBean - method forEachInputWithMatchingBase - throws a NullPointerException as base is found null for the dynamic property.

In current snapshot 2.3 and also in version 2.1 and 2.2 error is present, the below code
if (valueReference.getBase().equals(base)) throws error as getBase() returns null.

<p:inputText value="#{mapObject[var1]}" />

In the method "forEachInputWithMatchingBase" - for the above case it gets ValueExpression as "#{mapObject[var1]}" - and when it reaches to resolve var1 - since it's a simple dynamic property name - it gets the base object as null on ValueReference object and then throws NullPointerException on valueReference.getBase().equals(base)

@BalusC
Member
BalusC commented Mar 31, 2016

A reproducer would be helpful.

When trying to reproduce this based on case 2 in showcase which is only altered like below

    <ui:repeat value="#{['number1','number2']}" var="var">
        <h:inputText value="#{validateClassLevelBean.product[var]}" />
    </ui:repeat>

I get a different exception (Mojarra 2.2.13 on Tomcat 8.0.32)

javax.el.PropertyNotFoundException: /test.xhtml @23,47 value="#{validateClassLevelBean.product[var]}": Property 'org.omnifaces.el.ExpressionInspector$FinalBaseHolder@10bbab87' not found on type com.example.Product
    at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111)
    at org.omnifaces.el.ExpressionInspector.getValueReference(ExpressionInspector.java:47)
    at org.omnifaces.taghandler.ValidateBean$6.invoke(ValidateBean.java:332)
    at org.omnifaces.taghandler.ValidateBean$6.invoke(ValidateBean.java:328)
    at org.omnifaces.util.Components$ForEach$1.visit(Components.java:500)
    at org.omnifaces.util.Components$ForEach$TypesVisitCallback.visit(Components.java:565)
    at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:151)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1689)
    at com.sun.faces.facelets.component.UIRepeat.visitChildren(UIRepeat.java:862)
    at com.sun.faces.facelets.component.UIRepeat.visitTree(UIRepeat.java:759)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1700)
    at javax.faces.component.UIForm.visitTree(UIForm.java:371)
    at org.omnifaces.util.Components$ForEach.invoke(Components.java:533)
    at org.omnifaces.util.Components$ForEach.invoke(Components.java:497)
    at org.omnifaces.taghandler.ValidateBean.forEachInputWithMatchingBase(ValidateBean.java:328)
    at org.omnifaces.taghandler.ValidateBean.access$400(ValidateBean.java:152)
    at org.omnifaces.taghandler.ValidateBean$3.run(ValidateBean.java:287)
    at org.omnifaces.taghandler.ValidateBean$ValidateBeanCallback.invoke(ValidateBean.java:431)
    at org.omnifaces.util.Events$3.invoke(Events.java:351)
    at org.omnifaces.util.Events$6.beforePhase(Events.java:385)
    at org.omnifaces.eventlistener.CallbackPhaseListener.beforePhase(CallbackPhaseListener.java:63)
    at com.sun.faces.lifecycle.Phase.handleBeforePhase(Phase.java:228)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:99)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:658)

@vijpan
vijpan commented Apr 1, 2016

Thanks BalusC for taking a look at it.

So i went ahead and created a small maven war poc project to help reproduce this error. Attach is the poc.war (as a zip file poc.zip) file which also contains "java sources" insides the poc.zip/sources directory.

I ran this against WebSphere Liberty Profile 8.5.5.8 which is JEE7 compliant server - it uses myfaces 2.2.9.

But the attached war could be run against any other compliant JEE7 server (use case i have uses CDI annotations also)

Here are the main artifacts of the poc.war

Java Sources:
a) test.TestOFController - ViewScoped bean

package test;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.inject.Named;


@Named
@ViewScoped
@SuppressWarnings("serial")
public class TestOFController implements Serializable {

    private TestOFBean testOFBean;

    public TestOFController() {
    }

    @PostConstruct
    public void initializeController() {
        this.testOFBean = new TestOFBean();
    }

    public String load() {
        if (testOFBean.getDataMap() == null) {
            Map<String, String> initialData = new LinkedHashMap<>();
            initialData.put("1", "abc");
            initialData.put("2", "123");
            testOFBean.setDataMap(initialData);
        }
        return null;
    }

    public String save(){       
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Save action executed"));
        return null;
    }

    public TestOFBean getTestOFBean() {
        return testOFBean;
    }

}

b) test.TestOFBean - normal serializable bean - against which i would like to run bean validation - it contains a Map

package test;

import java.io.Serializable;
import java.util.Map;

@SuppressWarnings("serial")
public class TestOFBean implements Serializable {

    private Map<String, String> dataMap;

    public Map<String, String> getDataMap() {
        return dataMap;
    }

    public void setDataMap(Map<String, String> dataMap) {
        this.dataMap = dataMap;
    }

}

XHTML source:
a) faces/test/testof.xhtml - Here i am using the above bean's map to display data and validateBean

    <ui:composition     xmlns="http://www.w3.org/1999/xhtml"
                        xmlns:h="http://xmlns.jcp.org/jsf/html"
                        xmlns:f="http://xmlns.jcp.org/jsf/core"
                        xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                        xmlns:c="http://xmlns.jcp.org/jsp/jstl/core" 
                        xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
                        xmlns:p="http://primefaces.org/ui"                          
                        xmlns:o="http://omnifaces.org/ui"
                    template="../template/templateLayout.xhtml">

        <ui:define name="title">
            Test Title
        </ui:define>            

        <ui:define name="metadata">
            <f:metadata>    
                <f:viewAction action="#{testOFController.load}"/>
            </f:metadata>           
        </ui:define>            


        <ui:define name="content">                              
            <h:form >           
                <p:messages id="messages" />  

                <p:panelGrid columns="2" >

                    <f:facet name="header">
                        Testing Map based dynamic key for class level bean validation through o:validateBean
                    </f:facet> 

                   <ui:repeat value="#{testOFController.testOFBean.dataMap.keySet()}" var="var">
                        <p:outputLabel value=" Test Dynamic Key #{var}: " for = "dynaKey" />
                        <p:inputText id="dynaKey" value="#{testOFController.testOFBean.dataMap[var]}" />
                   </ui:repeat>

                    <f:facet name="footer">                      
                        <p:commandButton value="Save" icon="ui-icon-check"  action="#{testOFController.save}" update="@form" />                             
                    </f:facet> 
                </p:panelGrid>

                <o:validateBean value="#{testOFController.testOFBean}" />                                 

            </h:form>                       

        </ui:define>
</ui:composition> 

In this poc i had yet to implement anything related to bean validation - and when i hit the "Save" command button in the above xhtml it throws the following error- error is against this code in ValidateBean -if (valueReference.getBase().equals(base)) - its finds getBase() as null hence this NullPointerException - this happens when it tries to traverse over the dynamic property "var"

Note: If i remove the o:validateBean tag handler - everything is fine.

java.lang.NullPointerException
    at org.omnifaces.taghandler.ValidateBean$6.invoke(ValidateBean.java:334)
    at org.omnifaces.taghandler.ValidateBean$6.invoke(ValidateBean.java:328)
    at org.omnifaces.util.Components$ForEach$1.visit(Components.java:500)
    at org.omnifaces.util.Components$ForEach$TypesVisitCallback.visit(Components.java:565)
    at org.apache.myfaces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:141)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1019)
    at javax.faces.component.UIComponentBase.visitTree(UIComponentBase.java:1191)
    at org.apache.myfaces.view.facelets.component.UIRepeat.visitTree(UIRepeat.java:1188)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1047)
    at javax.faces.component.UIComponentBase.visitTree(UIComponentBase.java:1191)
    at javax.faces.component.UIForm.visitTree(UIForm.java:345)
    at org.omnifaces.util.Components$ForEach.invoke(Components.java:533)
    at org.omnifaces.util.Components$ForEach.invoke(Components.java:497)
    at org.omnifaces.taghandler.ValidateBean.forEachInputWithMatchingBase(ValidateBean.java:328)
    at org.omnifaces.taghandler.ValidateBean.access$400(ValidateBean.java:152)
    at org.omnifaces.taghandler.ValidateBean$3.run(ValidateBean.java:287)
    at org.omnifaces.taghandler.ValidateBean$ValidateBeanCallback.invoke(ValidateBean.java:431)
    at org.omnifaces.util.Events$3.invoke(Events.java:351)
    at org.omnifaces.util.Events$6.beforePhase(Events.java:385)
    at org.omnifaces.eventlistener.CallbackPhaseListener.beforePhase(CallbackPhaseListener.java:63)
    at org.apache.myfaces.lifecycle.PhaseListenerManager.informPhaseListenersBefore(PhaseListenerManager.java:77)
    at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:184)
    at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:143)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:198)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1287)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:778)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:475)
    at com.ibm.ws.webcontainer.filter.WebAppFilterChain.invokeTarget(WebAppFilterChain.java:146)
    at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:93)
    at org.omnifaces.filter.FacesExceptionFilter.doFilter(FacesExceptionFilter.java:91)
    at org.omnifaces.filter.HttpFilter.doFilter(HttpFilter.java:108)
    at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
    at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:90)
    at com.ibm.ws.security.jaspi.JaspiServletFilter.doFilter(JaspiServletFilter.java:57)
    at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
    at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:90)
    at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:1020)
    at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1142)
    at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.__handleRequest(CacheServletWrapper.java:81)
    at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java)
    at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:928)
    at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.run(DynamicVirtualHost.java:262)
    at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink$TaskWrapper.run(HttpDispatcherLink.java:955)
    at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.ready(HttpDispatcherLink.java:341)
    at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:470)
    at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleNewRequest(HttpInboundLink.java:404)
    at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.processRequest(HttpInboundLink.java:284)
    at com.ibm.ws.http.channel.internal.inbound.HttpICLReadCallback.complete(HttpICLReadCallback.java:66)
    at com.ibm.ws.tcpchannel.internal.WorkQueueManager.requestComplete(WorkQueueManager.java:504)
    at com.ibm.ws.tcpchannel.internal.WorkQueueManager.attemptIO(WorkQueueManager.java:574)
    at com.ibm.ws.tcpchannel.internal.WorkQueueManager.workerRun(WorkQueueManager.java:929)
    at com.ibm.ws.tcpchannel.internal.WorkQueueManager$Worker.run(WorkQueueManager.java:1018)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

poc.zip

@vijpan
vijpan commented Apr 20, 2016

Will this fix get into the 2.4 or it's not considered?

@BalusC
Member
BalusC commented Apr 20, 2016

As long as the issue is open, it will be considered :)

We are lately just busy with our own duties. Moreover, when dealing with code I didn't write myself, it also takes a bit more time to wrap my head around and make sense of it.

The reproducer is much appreciated.

@vijpan
vijpan commented Apr 20, 2016

Thanks for the quick response. Great to hear that it will not fall through the cracks.

@BalusC BalusC closed this in f4c74cd Apr 28, 2016
@BalusC
Member
BalusC commented Apr 28, 2016

Problem appears to be two-fold. First problem was relatively trivial, ExpressionInspector had to be altered to also check for FinalBaseHolder in dynamic properties. Second problem was that collecting bean properties in ui:repeat failed because value reference was determined before iteration instead of during iteration. Both problems are fixed.

Can you give today's 2.4-SNAPSHOT a try and let me know?

@vijpan
vijpan commented May 1, 2016

Yes it worked fine with 2.4-SNAPSHOT. Thanks for the fix!

@vijpan
vijpan commented Jul 1, 2016

Tested this against 2.4 today - and it's giving a different problem now.

ValidateBean class - line # 334 - returns false.
Line 334 -if (valueReference.getBase().equals(base)) {

The value of "valueReference.getBase()" - comes out to be the "map" object while the value of "base" is the object in which map object is present - so they will never be the same.

i.e. "base" object = #{testOFController.testOFBean}" while
"valueReference.getBase()" object = #{testOFController.testOFBean.dataMap}"

The same reproducer can work - change will be omnifaces upgrade to 2.4.

Do i need to create a separate issue or this issue can be reopened?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment