В приложении JSF (на основе Payara 5.183) я использую шаблоны, подобные приведенным ниже, для перенаправления пользователя после некоторого действия:
@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";
}
...
}
Проблема: в случае двух или болеестраницы были открыты с различными _model
объектами - действие delete()
вызывает NPE в _model.getId()
на других страницах после первого выполнения.
Между тем подход, подобный приведенному ниже, работает нормально:
@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");
}
...
}
Я записал видео 30сек с проблемой
Также пример проекта опубликован на GitHub
В чем причина NPE,и каков наиболее правильный способ использования навигации после некоторых действий в таком сценарии?
Спасибо!
PS Темы типа В чем разница между перенаправлением и навигацией /вперед и когда использовать что? Я уже прочитал, но ответа на мой вопрос пока не найдено.
Обновление 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)
....
Шаги для воспроизведения проблемы:
- Откройте файл main.xhtml
- Открыть обе ссылки на двух вкладках (Model1 и Model2) одновременно
- Нажмите кнопку «Удалить» на первой вкладке с Model1 - все отлично
- Нажмите «Удалить»кнопка на второй вкладке с Model2 - NPE, как
_model
равно null
(также я заметил, что @PostConstruct
также запускается)
Обновление 2:
Тема была обновлена, чтобы отразить более фундаментальную причину проблемы.Спасибо за ссылки в комментариях к подобным постам.