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:
- Open main.xhtml
- Open both links in two tabs (Model1 and Model2) simultaneously
- Press "Delete" button in the first tab with Model1 - everything fine
- Press "Delete" button in the second tab with Model2 - NPE as
_model
equalsnull
(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.