1

I have an application that is using JSF 2.2 on payara 4.1.9.2 with

 <context-param>
        <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
        <param-value>true</param-value>
 </context-param>

Here everything is working as expected.

Now I`m trying to update it to Payara 5.1.9.3 that is running with Mojarra 2.3.9.payara-p3.

The problem that I'm facing now is that if a ViewScope bean that has parameters set with f:viewParam on the creation of the bean is used to fire ajax requests the viewParams are lost (normal) and the model IS updated with NULL values.

So initially the viewParams works ok, the values are taken from the GET and the model is set accordingly.When the first ajax request is fired in the UPDATE_MODEL_VALUES PHASE the bean attribute value is set with the old value (which is OK), but on the second ajax request also in PHASE 4 the attribute value is updated with NULL.

I know that there are a lot of posts on this topic (f:viewParams are lost on ajax requests), but what is not clear to me is:

Why if I disable the javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL directive everything is ok, and if I enable it not anymore?

PS. with Payara 4.1.2.174 and Mojarra 2.2.14 is working

Update:

here we have the view of counter.xhtml

<ui:composition template="../layouts/user.xhtml"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:p="http://primefaces.org/ui"
  xmlns:sec="http://www.springframework.org/security/tags">

<f:metadata>
    <f:viewParam name="id" value="#{counterMB.id}"/>        
</f:metadata>

<ui:param name="currentMB" value="#{counterMB}"/>   
<ui:define name="title">Counter </ui:define>

<h:form id="myForm">
    Id is <h:outputText value="#{counterMB.id}" id="outputTxt"/>

    <p:commandLink 
            action="#{counterMB.incrementCounter()}"
            value="Increment"
            update="outputTxt"
    />

    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</h:form>   

and the bean :

@Named
@Scope("view")
public class CounterMB extends GenericMB implements Serializable{

    private static final long serialVersionUID = 1L;


    private Integer id;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        System.out.println("Setting the id with:"+id);
        this.id = id;
    }



    public void incrementCounter() {        

        id = id +1;

        // do something with id
        System.out.println("Id is="+Integer.toString(id));      
    }
}  

and some logs :

http://localhost:8080/myapp/counter.xhtml?id=4

(LifecycleListener.java:22) - START PHASE RESTORE_VIEW 1
(LifecycleListener.java:26) - END PHASE RESTORE_VIEW 1
(LifecycleListener.java:22) - START PHASE APPLY_REQUEST_VALUES 2
(LifecycleListener.java:26) - END PHASE APPLY_REQUEST_VALUES 2
(LifecycleListener.java:22) - START PHASE PROCESS_VALIDATIONS 3
(LifecycleListener.java:26) - END PHASE PROCESS_VALIDATIONS 3
(LifecycleListener.java:22) - START PHASE UPDATE_MODEL_VALUES 4

2019-10-24T13:43:52.573+0000|Info: Setting the id with:4
(LifecycleListener.java:26) - END PHASE UPDATE_MODEL_VALUES 4
(LifecycleListener.java:22) - START PHASE INVOKE_APPLICATION 5
(LifecycleListener.java:26) - END PHASE INVOKE_APPLICATION 5
(LifecycleListener.java:22) - START PHASE RENDER_RESPONSE 6
(LifecycleListener.java:26) - END PHASE RENDER_RESPONSE 6


//click on increment 1st time
(LifecycleListener.java:22) - START PHASE RESTORE_VIEW 1
(LifecycleListener.java:26) - END PHASE RESTORE_VIEW 1
(LifecycleListener.java:22) - START PHASE APPLY_REQUEST_VALUES 2
(LifecycleListener.java:26) - END PHASE APPLY_REQUEST_VALUES 2
(LifecycleListener.java:22) - START PHASE PROCESS_VALIDATIONS 3
(LifecycleListener.java:26) - END PHASE PROCESS_VALIDATIONS 3
(LifecycleListener.java:22) - START PHASE UPDATE_MODEL_VALUES 4
2019-10-24T13:44:07.013+0000|Info: Setting the id with:4
(LifecycleListener.java:26) - END PHASE UPDATE_MODEL_VALUES 4
(LifecycleListener.java:22) - START PHASE INVOKE_APPLICATION 5
2019-10-24T13:44:07.014+0000|Info: Id is=5
(LifecycleListener.java:26) - END PHASE INVOKE_APPLICATION 5
(LifecycleListener.java:22) - START PHASE RENDER_RESPONSE 6
(LifecycleListener.java:26) - END PHASE RENDER_RESPONSE 6


// click on icrement the second time
(LifecycleListener.java:22) - START PHASE RESTORE_VIEW 1
(LifecycleListener.java:26) - END PHASE RESTORE_VIEW 1
(LifecycleListener.java:22) - START PHASE APPLY_REQUEST_VALUES 2
(LifecycleListener.java:26) - END PHASE APPLY_REQUEST_VALUES 2
(LifecycleListener.java:22) - START PHASE PROCESS_VALIDATIONS 3
LifecycleListener.java:26) - END PHASE PROCESS_VALIDATIONS 3
(LifecycleListener.java:22) - START PHASE UPDATE_MODEL_VALUES 4
2019-10-24T13:44:12.548+0000|Info: Setting the id with:null
(LifecycleListener.java:26) - END PHASE UPDATE_MODEL_VALUES 4
(LifecycleListener.java:22) - START PHASE INVOKE_APPLICATION 5
2019-10-24T13:44:12.549+0000|Warning: #{counterMB.incrementCounter()}: java.lang.NullPointerException
javax.faces.FacesException: #{counterMB.incrementCounter()}: java.lang.NullPointerException
    at com.sun.faces.application.ActionListenerImpl.getNavigationOutcome(ActionListenerImpl.java:96)
    at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:71)
    at org.springframework.faces.webflow.FlowActionListener.processAction(FlowActionListener.java:71)
    at org.springframework.faces.model.SelectionTrackingActionListener.processAction(SelectionTrackingActionListener.java:64)
    at org.primefaces.application.DialogActionListener.processAction(DialogActionListener.java:54)
    at javax.faces.component.UICommand.broadcast(UICommand.java:222)
    at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:847)
    at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1395)
    at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:58)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:76)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:177)
    at javax.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:707)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:451)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1628)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:339)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:209)
    at org.glassfish.tyrus.servlet.TyrusServletFilter.doFilter(TyrusServletFilter.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:251)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:209)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:155)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:251)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:209)
    at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:251)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:209)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:755)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:575)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:159)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:520)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:217)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:182)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:156)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:218)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:95)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:260)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:177)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:109)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:88)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:53)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:524)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:89)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:94)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:33)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:114)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:569)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549)
    at java.lang.Thread.run(Thread.java:748)
Caused by: javax.faces.el.EvaluationException: java.lang.NullPointerException
    at com.sun.faces.application.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:76)
    at com.sun.faces.application.ActionListenerImpl.getNavigationOutcome(ActionListenerImpl.java:82)
    ... 82 more
Caused by: java.lang.NullPointerException
    at com.myapp.jsf.user.CounterMB.incrementCounter(CounterMB.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at javax.el.ELUtil.invokeMethod(ELUtil.java:263)
    at javax.el.BeanELResolver.invoke(BeanELResolver.java:494)
    at javax.el.CompositeELResolver.invoke(CompositeELResolver.java:215)
    at com.sun.el.parser.AstValue.invoke(AstValue.java:285)
    at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:304)
    at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:65)
    at com.sun.faces.application.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:66)
    ... 83 more

2019-10-24T13:44:12.551+0000|Info: INFO [http-thread-pool::http-listener-1(2)] (LifecycleListener.java:26) - END PHASE INVOKE_APPLICATION 5

A workaround would be to have rendered="#{not facesContext.postback}" on f:viewParam, but I don't think this is the correct behavior.

Update:
At Kukeltje sugestion I have tried with a plain JSF app, with @ViewScoped and it works as expected.With this in isght I'll continue adding primefaces and spring and .... everything that I have in the current app to try simulate the current situation.

Update:

The reason it worked with plain jsf was that h:commandLink was not issuing an ajax request. I was able to reproduce it with these files:

`

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.org/ui">

    <h:head>
        <f:facet name="first">
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />         
        </f:facet>      
        <title> Simulator - Counter rest</title>
    </h:head>

    <h:body>    
        <f:metadata>
            <f:viewParam name="id" value="#{counterMB.id}"/>        
        </f:metadata>

        <h:form id="myForm">
            Id is <h:outputText value="#{counterMB.id}" id="outputTxt"/>

            <h:commandButton value="Increment"update="outputTxt">           
                <f:ajax listener="#{counterMB.incrementCounter()}" execute="myForm" render="outputTxt"/> 
            </h:commandButton>
        </h:form>         
    </h:body>
</html>

`
and with this bean

import java.io.Serializable;

import javax.faces.view.ViewScoped;
import javax.inject.Named;

@Named
@ViewScoped
public class CounterMB implements Serializable{

    private static final long serialVersionUID = 1L;

    private Integer id; 

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        System.out.println("Setting the id with:"+id);
        this.id = id;
    }

    public void incrementCounter() {
        id = id +1;
        System.out.println("Id is="+Integer.toString(id));      
    }
}
Videanu Adrian
  • 972
  • 3
  • 16
  • 41
  • I have no idea but I'd like to investigate. For that you need to do what is requested in [ask]... That is to create a [mcve]. Cheers. Ufortunately I'm off for a one week holiday tonight, so I might not be able to help at short notice – Kukeltje Oct 24 '19 at 12:41
  • 1: this is **not** a jsf view scoped bean. Are you saying it works when usong a real cdi managed bean? if so, spring is very relevant tag if not, code can be removed here 2: Are you saying it works when you remove the csrf token field? 3: the same for not using includes/layout (see http://www.stackoverflow.com/tags/jsf/info) 4: same when not extending the GenericMB. 5: Please make it a real [mcve] (stll on holiday) – Kukeltje Oct 30 '19 at 09:05
  • Addition: ypu did try more than others normally would.. So thanks for that! (Sent from holiday) – Kukeltje Oct 30 '19 at 09:11
  • Also check https://stackoverflow.com/questions/31068678/which-xml-namespace-to-use-with-jsf-2-2-and-up and change this. Not saying it is related, but always good to keep up to date – Kukeltje Oct 30 '19 at 17:12

1 Answers1

0

I have been searching a little and found 3 things (not sure this is the or even an answer at all, but it is too much to type in comments)

  1. is a 'duplicate' using javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL causes viewParam to be null (unanswered but some discussion)
  2. is a link that seems to be related includeViewParams=true converts null model value to empty string in query string
  3. is some info from http://showcase.omnifaces.org/components/viewParam about a change in JSF 2.3 related to viewParam and null.

    In JSF 2.3, this has been fixed and has only effect when javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL context param is set to true.

This combination leads me to think that you 'were lucky' it worked in 2.2 (sure id was not '0' on postbacks?) And honestly, the 2.3 behaviour 'feels' correct.

As a solution you could also add a 'null' check in the setter

void setId(Integer id) {
    if (id != null) {
        this.id = id;
    }
}

Or you could try the o:viewParam and see if it makes a difference.

Kukeltje
  • 12,223
  • 4
  • 24
  • 47
  • hi Kukeltje, "This combination leads me to think that you 'were lucky' it worked in 2.2 (sure id was not '0' on postbacks?) And honestly, the 2.3 behaviour 'feels' correct." -- the id was not 0 for sure as if it was that would reset my already seted bean values. Anyway, i`ll accept the answer as this helped me a lot. thanks. – Videanu Adrian Nov 05 '19 at 06:12
  • 1
    I would go with . I guess it would be more elegant than adding a not null check on each setter. Also easier to update as I can search and replace in view directly – Videanu Adrian Nov 05 '19 at 11:46
  • I'll add my experience to this: 1. The core component f:viewParam does not support the rendered tag and 2. Changing to OmniFaces o:viewParam seems to fix it without configured the rendered attribute. – matsa Apr 27 '21 at 08:01