1

In a JSF application (Payara 5.183 based) I am using patterns like below for redirecting user after some action:

@Named
@ViewScoped
public class ModelViewBean implements Serializable {
    private Model _model;
    ...
    public String delete() {
        System.out.println(">> Deleting model with ID: " + _model.getId());
        _appDaoBean.daoDelete(_model);
        return "/main.xhtml?faces-redirect=true";
    }
    ...
}

The issue: in case there are two or more pages were open with different _model objects - action delete() causes NPE in _model.getId() on other pages after first execution.

Meanwhile the approach like below works fine:

@Named
@ViewScoped
public class ModelViewBean implements Serializable {
    private Model _model;
    ...
    public void delete() {
        System.out.println(">> Deleting model with ID: " + _model.getId());
        _appDaoBean.daoDelete(_model);
        FacesContext.getCurrentInstance().getExternalContext().redirect("/main.xhtml");
    }
    ...
}

I have recorded the 30sec video with the issue

Also the sample project is published on the GitHub

What is the reason of the NPE, and what is the most proper way of using navigation after some action in such scenario?

Thank you!

P.S. Topics like What is the difference between redirect and navigation/forward and when to use what? I have already read but no answer for my question found so far.

Update 1:

main.xhtml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui">

    <h:body>
        <h:form id="f1">

            <h2>Main Page</h2>

            <p:link outcome="/model.xhtml" value="Model1">
                <f:param name="id" value="1"/>
            </p:link>

            <br/>

            <p:link outcome="/model.xhtml" value="Model2">
                <f:param name="id" value="2"/>
            </p:link>

        </h:form>
    </h:body>
</html>

model.xhtml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <f:view>
        <f:metadata>
            <f:viewParam name="id" value="#{modelViewBean.id}" />
            <f:viewAction action="#{modelViewBean.initModel}" />
        </f:metadata>
        <h:body>
            <h:form id="f1">

                <p:outputLabel value="Model ID: #{modelViewBean.model}" />
                <br/>
                <p:commandButton value="Delete" action="#{modelViewBean.delete()}" process="@this" update="@form" />

            </h:form>
        </h:body>
    </f:view>
</html>

ModelViewBean.java

....
import javax.faces.view.ViewScoped;
....

@Named
@ViewScoped
public class ModelViewBean implements Serializable {

    private static final long serialVersionUID = 6400111954793903238L;

    private String _id;
    private String _model;
    private Date _beanCreateTime;


    @PostConstruct
    private void init() {
        System.out.println(">> @PostConstruct -> init()");
        _beanCreateTime = new Date();
    }


    public String initModel() {
        System.out.println(">> ViewAction -> initModel()");
        if (_id == null || _id.trim().isEmpty()) {
            return "/main.xhtml?faces-redirect=true";
        }
        _model = UUID.randomUUID().toString();
        return null;
    }


    public String delete() {
        System.out.println(">> Deleting model with ID: " + _model.toUpperCase());
        return "/main.xhtml?faces-redirect=true";
    }


    public String getId() {
        return _id;
    }


    public void setId(String id) {
        this._id = id;
    }


    public String getModel() {
        return _model;
    }


    public Date getBeanCreateTime() {
        return _beanCreateTime;
    }
}

NPE:

java.lang.NullPointerException
at local.jsfsample.ModelViewBean.delete(ModelViewBean.java:40)
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)
....

Steps for reproducing the issue:

  1. Open main.xhtml
  2. Open both links in two tabs (Model1 and Model2) simultaneously
  3. Press "Delete" button in the first tab with Model1 - everything fine
  4. Press "Delete" button in the second tab with Model2 - NPE as _model equals null (also I have noticed that @PostConstruct is being triggered as well)

Update 2:

The topic was updated in order to reflect more fundamental cause of the issue. Thanks for links in comments to the similar posts.

AndrewG10i
  • 661
  • 1
  • 6
  • 21
  • JSF 2.4 does not exist as an official release. It is a weird (required by Oracle) side effect of moving all Java EE code to Eclipse. Example code (in [mcve] flavour, should be posted inline. And please post the stacktrace with the NPE and explicitly mention on which line the NPE occurs... – Kukeltje Sep 24 '18 at 11:29
  • 1
    And, are you indeed implying that the NPE doesn't occur when the bean is in a different scope? – BalusC Sep 24 '18 at 12:12
  • @BalusC thank you for your question! Based on the usage scenario - I can not use any other scopes, as I need to open two similar pages with different models (entities) simultaneously. Thus trying to workaround usage of the `@ViewScoped` bean - doesn't really help (even it could work fine). – AndrewG10i Sep 25 '18 at 02:01
  • @Kukeltje thank you for your time checking my post! I have posted the exact piece of code that contains a root cause of the issue. Whole sample project code available following the link on GitHub. Stack-trace is pretty plain, nothing special at all. Also, the issue itself becoming super self-explaining by reviewing the short video I have attached. Thank you again for your time! P.S. The issue can be reproduced on the Glassfish 5 with JSF 2.3.2 as well. – AndrewG10i Sep 25 '18 at 02:08
  • **try** with other scopes. Of it fails there too, your title is not right and people might start trting solutiins un wrong directions. And again please post an [mcve] **inline** – Kukeltje Sep 25 '18 at 06:37
  • @Kukeltje, okay, will do. Will post results shortly. Thanks! – AndrewG10i Sep 25 '18 at 07:17
  • Wow.. so many typo's in my previous comment from my phone ;-) – Kukeltje Sep 25 '18 at 07:30
  • I have updated the post with Minimal, Complete, and Verifiable example. The is no possibility to use other scopes in this specific case except ViewScope, as several pages should be openned the same time with the same bean. Any input and ideas are highly appreciated. Thanks! – AndrewG10i Sep 25 '18 at 11:34
  • Thanks, I just got an error in the xhtml. Seems like an `h:head` is missing. And can you try and see if using `@ManageBean` and a `@Viewscoped` from `javax.faces.bean.ViewScoped` works instead of the CDI ones you now use? This https://stackoverflow.com/questions/47907080/multiple-browser-tabs-or-windows-with-the-same-viewscoped-bean-class seems to have a sort of the same issue. – Kukeltje Sep 25 '18 at 11:45
  • And also check https://stackoverflow.com/questions/33078064/re-execute-fviewaction-when-viewscoped-bean-is-recreated-following-a-post-reque. Seems sort of related to. And it might that a 'windowScoped' bean is a better fit: https://deltaspike.apache.org/documentation/jsf.html#@WindowScoped – Kukeltje Sep 25 '18 at 12:02
  • @Kukeltje Thank you so much for your efforts helping me with this issue! Will check the suggested cases as well and reply back shortly! And yes, this is exactly my case: https://stackoverflow.com/questions/47907080/multiple-browser-tabs-or-windows-with-the-same-viewscoped-bean-class – AndrewG10i Sep 25 '18 at 14:53
  • You are welcome. It is remarkable that in the last week there are 3 people posted questions with related issues https://stackoverflow.com/questions/52479878/org-omnifaces-cdi-viewscoped-not-working-correctly-with-multiple-tabs-workarou. This last one switched to omnifaces viewscoped only running into some other 'issue'. All related (imo) to (ab)using a scope in the wrong context. ;-) – Kukeltje Sep 25 '18 at 14:57
  • @Kukeltje, I read your comments and now I see that the actual issue is a little-bit different to what I was thinking about originally. Indeed, for me it was like Tab = View, but it seems to be a wrong assumption and in fact Tab != View!! In that way what is the best practice of handling several tabs in JSF? Looks like a `@WindowScoped` can be a solution here, will need to test. – AndrewG10i Sep 25 '18 at 15:20
  • How is your jsf configured? Number of views etc? Oh and the windowscope is the best solution but a conversationscope could work too – Kukeltje Sep 25 '18 at 18:08
  • Okay, so I played around with different options and variations, even found such an interesting thing like `javax.faces.CLIENT_WINDOW_MODE`, but anyway nothing helped. Meanwhile, I also noticed, that redirecting using handleNavigation pattern actually calls `@PreDestroy` on the `@javax.faces.view.ViewScoped` bean related to the current Tab, and mystically it somehow unloads (destroys) all other ViewScoped beans of the same class (as executing action on the 2nd Tab goes via workflow: @PostConstruct -> delete() -> @PreDestroy). – AndrewG10i Sep 29 '18 at 13:57
  • Then I tried `@org.omnifaces.cdi.ViewScoped` - and it actually works absolutely properly! It calls `@PreDestroy` exactly on the ViewScoped bean associated with the current tab only, without affecting ViewScoped beans of the same class on other tabs! So I got the behavior exactly as expected from the ViewScoped bean! So far looks like the issue solved! – AndrewG10i Sep 29 '18 at 14:00
  • ...after additional investigation: looks like the JSF itself sees only one instance of `@javax.faces.view.ViewScoped` bean (to be more specific: the instance associated with the last opened Tab), as for example, by executing the following scenario: 1. Open two Tabs; 2. Unload app from server; - in result only one `@PreDestroy` method is executed!! Meanwhile with the `@org.omnifaces.cdi.ViewScoped` - two `@PreDestroy` methods are executed: each per instance of such bean!! Thus, in case of the `@javax.faces.view.ViewScoped` for me it looks like more a BUG rather than a feature... – AndrewG10i Sep 29 '18 at 14:25
  • See: https://github.com/payara/Payara/issues/2506 this is fixed in Payara 5.184 – codylerum Jan 04 '19 at 17:49

1 Answers1

2

I've run into this as well after moving from Wildfly 11 -> Wildfly 14 ( Mojarra 2.2.13.SP4 -> 2.3.5.SP2 )

This demo project works on Wildfly 11 but fails on Wildfly 14.

It would appears that the references in the ViewMap are removed but the actual ViewScoped beans are not destroyed.

I've opened https://issues.jboss.org/browse/WFLY-11275 to hopefully track this down.

codylerum
  • 218
  • 2
  • 7
  • I like the way how `@org.omnifaces.cdi.ViewScoped` is implemented. You may give it a try as well. – AndrewG10i Nov 25 '18 at 12:05
  • This seems to have been fixed in WildFly 16 when I looked at the link in the answer. They backported a fix for Mojarra 2.3.9 to the 2.3.5SPx version that is used by WildFly. – Kukeltje Feb 16 '19 at 11:01
  • At this point I don't believe the issue is fixed in the main Mojarra. It was fixed in Payara and then that same fixed has been used to create a patched version for Wildfly 16.0.0.Beta1. The issue for Mojarra remains open. https://github.com/eclipse-ee4j/mojarra/issues/4509 – codylerum Feb 16 '19 at 23:02