Я работаю над внедрением кластеризации в веб-приложение Spring-Boot на основе JSF, и как только мы включили репликацию сеансов с помощью Hazelcast, мы начали замечать, что некоторые из наших страниц JSF, использующих bean-компоненты ViewScoped, перестали работать правильно.Если мы отключим репликацию сеанса и Hazelcast, странное поведение больше не будет происходить.
Я впервые заметил проблему на одной из наших страниц, которая использует компонент PrimeFaces Wizard.Значения, введенные на первой странице мастера, теряются при "отправке" второй страницы.
Затем на другой странице я заметил, что командная кнопка больше не вызывает метод actionListener на управляемом бине.Я установил точку останова в методе, и точка останова никогда не срабатывает, но страница «мигает» и обновляется до исходного состояния.Я заметил, что метод PostConstruct для управляемого компонента не вызывается снова, поэтому он не генерирует новый экземпляр компонента ViewScoped.
Ни одна из этих проблем не возникает, когда я отключаю репликацию сеанса и Hazelcast.Насколько я могу судить, осматривая сеанс и его содержимое, кажется, что сеансы создаются и должным образом сохраняются, насколько я могу судить.
Приложение представляет собой веб-приложение Spring-Boot, использующеесоединения начнут вводить JSF 2.3.7 (Mojarra), PrimeFaces 6.2 и Omnifaces 1.14.1.Изначально мы разработали приложение без какой-либо репликации сеанса, и у нас не было проблем с нашими bean-компонентами ViewScoped.
Бины ViewScoped используют аннотацию org.springframework.stereotype.Component, как вы видите в примерах joinfaces.и javax.faces.view.ViewScoped в качестве аннотации области видимости.Я также пытался ввести Weld и использовать аннотацию @Named, а также использовать старые устаревшие аннотации JSF @ManagedBean и @ViewScoped, но такое же поведение сохраняется во всех случаях.
Я прошел и убедился, что все наши ManagedBeans полностью сериализуемы, а также любые атрибуты на самих bean-компонентах.
Чтобы продемонстрировать то, что я вижу, я выбрал два очень простых примера из парыразмещает в Интернете и создал простой проект Spring-Boot, который можно клонировать и запускать самостоятельно.
https://github.com/illingtonFlex/ViewScopeDemo
Это демонстрационное приложение содержит два управляемых bean-компонента и два файла xhtml.
Первый пример - тот, который скопирован из примера на веб-сайте BalusC: http://balusc.omnifaces.org/2010/06/benefits-and-pitfalls-of-viewscoped.html
Файл xhtml выглядит следующим образом:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Really simple CRUD</title>
</h:head>
<h:body>
<h3>List items</h3>
<h:form rendered="#{not empty viewScopedController.list}">
<h:dataTable value="#{viewScopedController.list}" var="item">
<h:column><f:facet name="header">ID</f:facet>#{item.id}</h:column>
<h:column><f:facet name="header">Value</f:facet>#{item.value}</h:column>
<h:column><h:commandButton value="edit" action="#{viewScopedController.doEdit(item)}" /></h:column>
<h:column><h:commandButton value="delete" action="#{viewScopedController.delete(item)}" /></h:column>
</h:dataTable>
</h:form>
<h:panelGroup rendered="#{empty viewScopedController.list}">
<p>Table is empty! Please add new items.</p>
</h:panelGroup>
<h:panelGroup rendered="#{!viewScopedController.edit}">
<h3>Add item</h3>
<h:form>
<p>Value: <h:inputText value="#{viewScopedController.item.value}" /></p>
<p><h:commandButton value="add" action="#{viewScopedController.add}" /></p>
</h:form>
</h:panelGroup>
<h:panelGroup rendered="#{viewScopedController.edit}">
<h3>Edit item #{viewScopedController.item.id}</h3>
<h:form>
<p>Value: <h:inputText value="#{viewScopedController.item.value}" /></p>
<p><h:commandButton value="save" action="#{viewScopedController.save}" /></p>
</h:form>
</h:panelGroup>
</h:body>
</html>
, и компонент ViewScoped, поддерживающий эту страницу, выглядит следующим образом:
package help.me.understand.jsf.ViewScopeDemo.controller;
import help.me.understand.jsf.ViewScopeDemo.model.Item;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Component
@ViewScoped
@Data
@EqualsAndHashCode(callSuper=false)
@ToString
public class ViewScopedController implements Serializable {
private static final Logger log = LoggerFactory.getLogger(ViewScopedController.class);
private List<Item> list;
private Item item = new Item();
private boolean edit;
@PostConstruct
public void init() {
// list = dao.list();
// Actually, you should retrieve the list from DAO. This is just for demo.
list = new ArrayList<Item>();
list.add(new Item(1L, "item1"));
list.add(new Item(2L, "item2"));
list.add(new Item(3L, "item3"));
}
public void add() {
// dao.create(item);
// Actually, the DAO should already have set the ID from DB. This is just for demo.
item.setId(list.isEmpty() ? 1 : list.get(list.size() - 1).getId() + 1);
list.add(item);
item = new Item(); // Reset placeholder.
}
public void doEdit(Item item) {
this.item = item;
edit = true;
}
public void save() {
// dao.update(item);
item = new Item(); // Reset placeholder.
edit = false;
}
public void delete(Item item) {
// dao.delete(item);
list.remove(item);
}
public List<Item> getList() {
return list;
}
public Item getItem() {
return item;
}
public boolean isEdit() {
return edit;
}
// Other getters/setters are actually unnecessary. Feel free to add them though.
}
Если вы запустите приложение и перейдете по адресу localhost: 8080 / index.xhtml, нажмите «Изменить» на одной из записей.Затем введите новое имя в текстовое поле и нажмите «Сохранить».Метод save для управляемого компонента никогда не вызывается, и страница «сбрасывается» в исходное состояние.Если вы отключите Hazelcast и репликацию сеанса, закомментировав аннотацию @EnableHazelcastHttpSession вместе с hazelcastInstance @Bean, определенным в ViewScopeDemoApplication, приведенные выше примеры шагов сработают.Вызывается метод сохранения, и имя редактируемого элемента изменяется.
Чтобы продемонстрировать еще один пример странного поведения ViewScoped, я скопировал дословный пример кода мастера из демонстрации PrimeFaces: https://www.primefaces.org/showcase/ui/panel/wizard.xhtml
Запустив приложение, вы можете получить доступ к этому примеру через localhost: 8080 / wizard.xhtml
При включенной Hazelcast и репликации сеансов вы можете установить точку останова в методе onFlowProcess, который срабатывает при переходе от одногоСтраница мастера на следующую.Вы можете видеть, что значения, введенные на первом шаге мастера, теряются (они становятся нулевыми) при последующих изменениях страницы мастера.Отключите Hazelcast, и значения сохранятся на всем протяжении вкладок мастера.
Я не вижу каких-либо ошибок или исключений в журналах при возникновении проблем. Также я не вижу проблем в консоли отладки браузера. Однако из этих двух примеров ясно, что bean-компоненты ViewScoped ведут себя по-разному в зависимости от того, включена ли репликация сеанса Hazelcast.
Заранее благодарим за помощь и внимание!