@PostConstruct вызывается несколько раз для bean-компонента @ConversationScoped

У меня есть bean-компонент @ConversationScoped с методом запуска, например:

@PostConstruct
public void start() {
    if (conversation.isTransient()) {
        conversation.begin();
        log.debug("conversation.getId(): " + conversation.getId());
    }
}

Моя проблема заключается в том, что каждый раз, когда страница обновляется, начинается новый диалог, новый диалог также запускается каждый раз, когда у меня есть вызов AJAX для метода в компоненте (что является моей главной проблемой).

То, что я действительно хочу, это чтобы разговор с Сэмом зависал до тех пор, пока я не вызову вручную. Что мне здесь не хватает?

6 ответов

Решение

Вы проверяли, что вызовы (AJAX) содержат параметр идентификатора разговора (cid)?

Если этого не произойдет, ожидается, что новый разговор начнется для каждого звонка.

Немного не по теме, но, надеюсь, ценно:

Я не уверен на 100%, что @PostConstruct - подходящее место для начала разговора. Я бы предпочел использовать Face-событие, как это:

<f:metadata>
        <f:event type="javax.faces.event.PreRenderViewEvent"
                listener="#{myBean.init}" />
</f:metadata>

и начните разговор, если вы уверены, что не находитесь в запросе обратной отправки JSF.

public void init() {
       if (!FacesContext.getCurrentInstance().isPostback() && conversation.isTransient()) {
          conversation.begin();
       }
    }

Если вы используете Seam 3, это еще проще:

<f:metadata>
   <s:viewAction action="#{myBean.init}" if="#{conversation.transient}" />
</f:metadata>

Концепция встроенного в JSR-299 разговора немного нарушена. По крайней мере, для приложений JSF. Использование @PreRenderViewEvent в основном отбросило бы все преимущества этого подхода, поскольку каждый компонент @ConversationScoped сделал бы longRunning.

Вместо этого вы можете попробовать использовать Apache MyFaces CODI @ConversationScoped. CODI - это библиотека расширений CDI, которая хорошо работает как с Apache OpenWebBeans, так и с Weld. Кроме того, он также предоставляет контексты @ViewScoped, @ViewAccessScoped (тип автоматического разговора) и @WindowScoped.

Больше на: https://cwiki.apache.org/confluence/display/EXTCDI/Index

Это все в документах:

Область разговора активна:

на всех стандартных этапах жизненного цикла любого запроса JSF о лицах или не лицах.

Контекст диалога обеспечивает доступ к состоянию, связанному с конкретным диалогом. С каждым запросом JSF связан диалог. Эта связь автоматически управляется контейнером в соответствии со следующими правилами:

Любой запрос JSF имеет ровно один связанный разговор. Диалог, связанный с запросом JSF, определяется в начале фазы представления восстановления и не изменяется во время запроса.

Любой разговор находится в одном из двух состояний: переходный или длительный.

По умолчанию разговор является временным. Переходный разговор может быть помечен как длительный с помощью вызова Conversation.begin(). Продолжительный диалог может быть помечен как временный с помощью Conversation.end().

Все длительные диалоги имеют строковый уникальный идентификатор, который может быть установлен приложением, когда диалог помечен как длительный, или сгенерирован контейнером.

Если диалог, связанный с текущим запросом JSF, находится в переходном состоянии в конце запроса JSF, он уничтожается, и контекст диалога также разрушается.

Если в конце запроса JSF диалог, связанный с текущим запросом JSF, находится в состоянии длительного выполнения, он не уничтожается. Вместо этого он может распространяться на другие запросы в соответствии со следующими правилами:

Длительный контекст диалога, связанный с запросом, который отображает представление JSF, автоматически распространяется на любой запрос лиц (отправка формы JSF), который исходит от этой отображаемой страницы. Длительный контекст диалога, связанный с запросом, который приводит к перенаправлению JSF (перенаправление, полученное в результате правила навигации или JSF NavigationHandler), автоматически распространяется на результирующий запрос не для лиц и на любой другой последующий запрос на тот же URL-адрес. Это достигается с помощью параметра запроса GET с именем cid, содержащего уникальный идентификатор диалога. Длительный диалог, связанный с запросом, может распространяться на любой запрос, не связанный с лицами, посредством использования параметра запроса GET с именем cid, содержащего уникальный идентификатор диалога. В этом случае приложение должно управлять этим параметром запроса.

Когда в запрос JSF не передается диалог, запрос связывается с новым временным диалогом. Все длительные разговоры ограничиваются определенным сеансом сервлета HTTP и могут не пересекать границы сеанса. В следующих случаях распространенный длительный диалог не может быть восстановлен и повторно связан с запросом:

Когда сеанс сервлета HTTP признан недействительным, все контексты продолжительного диалога, созданные во время текущего сеанса, уничтожаются после завершения метода servlet service(). Контейнеру разрешено произвольно уничтожать любой продолжительный диалог, связанный с текущим запросом JSF, для сохранения ресурсов.

Автор: Гэвин Кинг, Пит Мьюир

ИМХО CDI разговоры разбиты по замыслу, и Стерберг указал на многообещающую альтернативу. В моем приложении у меня та же проблема, и в настоящее время я рефакторинг его в CDI + CODI 1, и он просто чувствует себя хорошо. @ConversationScoped решает все эти проблемы. При рефакторинге моего приложения я мог решить множество неприятных случаев с помощью @ViewAccessScoped. Спасибо, Струберг, за то, что указал нам на это!

Как ни странно, если вы добавите прослушиватель событий в ваш Facelet, даже если он вызывает пустой метод, действие формы сгенерированного источника будет иметь параметр 'cid' и, следовательно, вызов AJAX не создаст новый диалог. Без прослушивателя событий в 'cid' отсутствует действие в форме.

<f:metadata>
    <f:event listener="#{myBean.dummy}" type="preRenderView" />
</f:metadata>

MyBean.java

public void dummy() {}
Другие вопросы по тегам