При использовании MyFaces Orchestra bean-компоненты dialog.access НЕ удаляются при переходе в другое представление - PullRequest
3 голосов
/ 28 июня 2011

Мы создаем приложение, используя JSF 2, Spring и Hibernate.MyFaces Orchestra используется для предоставления области диалога, которую мы используем для большинства страниц приложения (чтобы воспользоваться преимуществами управления Orchestra сеансом Hibernate).Все наши bean-компоненты объявлены для использования области многообещающего разговора, что (согласно документации Orchestra) должно означать, что bean-компоненты удаляются из области действия, как только пользователь переходит на страницу, которая не содержит ссылок на этот экземпляр вспомогательного компонента..

Проблема, с которой я сталкиваюсь, состоит в том, что, если я перемещаюсь по пути из представления без явного аннулирования диалога, если они возвращаются к этому представлению позже, он все еще имеет те же данные, что и раньше.Я реализовал методы ConversationBindingListener во всех своих компонентах поддержки, и я могу видеть, когда они удаляются из диалога, и я вижу, что они НЕ во многих случаях.

Что делает проблему более запутанной, так это то, чтовспомогательные компоненты удаляются, когда я перехожу на некоторые страницы (просмотры), но не на другие.Я подумал, может быть, это потому, что у страниц была непреднамеренная ссылка на другие компоненты поддержки в EL, но я не смог их найти.Я также подумал, что, возможно, эта проблема возникла только тогда, когда я перешел с одной страницы, на которой находился bean-объект theme.access, на другую страницу, используя другой bean-компонент message.scoped.Однако в тех случаях, когда она удаляется из диалога, на этой странице также содержатся ссылки на bean-объектcope.occess.

Как я уже говорил ранее, явная аннулирование диалога с помощью Conversation.getCurrentInstance (). Invalidate ().работает.Однако явная аннулирование диалога не возможно для каждого варианта использования, поскольку это будет очень распространенный случай, когда пользователь может покинуть представление, просто нажав одну из навигационных ссылок.

ДОПОЛНИТЕЛЬНЫЕ ДАННЫЕ: Мы 'мы использовали Hibernate 3.6 (вместо JPA), поэтому нам пришлось использовать HibernatePersistenceContextFactory .

  • MyFaces Orchestra (myfaces-orchestra-core20-1.4.jar)
  • JSF 2 (Mojarra 2.0.4)
  • Spring 3.0
  • PrimeFaces 2.2.1
  • RichFaces 4.0.0

Здеськак выглядит моя конфигурация контекста Spring (для Orchestra).

<!-- 1. initialization of all orchestra modules (required for core15 module) -->
<import resource="classpath*:/META-INF/spring-orchestra-init.xml" />

<!-- 2. the conversation scopes -->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
        <map>
            <entry key="conversation.manual">
                <bean
                    class="org.apache.myfaces.orchestra.conversation.spring.SpringConversationScope">
                    <property name="timeout" value="30" />
                    <property name="advices">
                        <list>
                            <ref bean="persistentContextConversationInterceptor" />
                        </list>
                    </property>
                </bean>
            </entry>
            <entry key="conversation.access">
                <bean
                    class="org.apache.myfaces.orchestra.conversation.spring.SpringConversationScope">
                    <property name="timeout" value="30" />
                    <property name="advices">
                        <list>
                            <ref bean="persistentContextConversationInterceptor" />
                        </list>
                    </property>
                    <property name="lifetime" value="access" />
                </bean>
            </entry>
        </map>
    </property>
 </bean>    


<!-- 3. the "entity manager" manager -->
<bean id="persistentContextConversationInterceptor"
    class="org.apache.myfaces.orchestra.conversation.spring.PersistenceContextConversationInterceptor">
    <property name="persistenceContextFactory" ref="persistentContextFactory" />
</bean>



<!-- 4. conversation - persistence adapter -->
<bean id="persistentContextFactory"
    class="com.acme.infra.orchestra.hibernate.HibernatePersistenceContextFactory">
    <property name="entityManagerFactory" ref="sessionFactory" />
</bean>

<!-- 5. persistence -->
<bean id="managedDataSource"
    class="org.apache.myfaces.orchestra.connectionManager.ConnectionManagerDataSource">
    <property name="dataSource" ref="dataSource" />
</bean>

Вот несколько примеров объявлений компонентов JSF для поддержки.

<bean id="quoteSummaryBackingBean" class="com.acme.ui.backing.QuoteSummaryBackingBean"
        scope="conversation.access" orchestra:conversationName="QuoteSummaryConversation">
    <property name="quotingBusinessService" ref="quotingBusinessService"/>
    <property name="customerBusinessService" ref="customerBusinessService"/>
    <property name="referenceDataBusinessService" ref="referenceDataBusinessService"/>
    <property name="quoteExportBusinessService" ref="quoteExportBusinessService" />
</bean>

<bean id="createQuoteBackingBean" class="com.acme.ui.backing.CreateQuoteBackingBean" 
        scope="conversation.access" orchestra:conversationName="CreateQuoteConversation">  
    <property name="quotingBusinessService" ref="quotingBusinessService"/>
    <property name="customerBusinessService" ref="customerBusinessService"/>
    <property name="referenceDataBusinessService" ref="referenceDataBusinessService"/>


Ответы [ 2 ]

2 голосов
/ 07 июля 2011

Это не самое элегантное решение, и я предполагаю, что оно может приводить к появлению новых ошибок (поскольку проверка, используемая Orchestra, предназначалась для обработки ситуаций с запросами AJAX).Я добавил новый метод к моим компонентам поддержки (используя базовый класс), который обрабатывает сброс org.apache.myfaces.orchestra.conversation.jsf.AccessScopePhaseListener: переменная области видимости oldView в null.

public void clearPreviousConversation() {
    if (firstHit) {
        String keyName = 
            "org.apache.myfaces.orchestra.conversation.jsf.AccessScopePhaseListener:oldView";

        FacesContext.getCurrentInstance().getExternalContext()
        .getRequestMap().put(keyName, null);

        firstHit = false;
    }
}   

ToУбедитесь, что этот метод вызывается только один раз за просмотр, у меня есть флаг "firstHit", который является логической переменной-членом.

Тогда, поскольку эта конкретная проблема проявляется только в представлениях, использующих метаданные f: я использую этот факт, чтобы вызывать этот метод только там, где это необходимо.Я добавляю его как вызов перед рендерингом в мои метаданные f:

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

Если вы используете f: viewParam или другие элементы события f:, вы можете смешать их вместе.

<f:metadata>
    <f:viewParam name="tabIndex" value="#{controlBackingBean.tabIndex}" />
    <f:event type="preRenderView" listener="#{controlPanelBackingBean.clearPreviousConversation}" />
    <f:event type="preRenderView" listener="#{controlPanelBackingBean.init}" />
</f:metadata>
0 голосов
/ 03 июня 2013

Как правильно сказал Бенито Вега в своем комментарии, проблема проявляется, когда вы выполняете GET-запрос к представлению, содержащему тег <f:metadata/>. Это потому, что оркестр AccessScopePhaseListener.doAfterRestoreView() различает POST и GET, тестируя FacesContext.getRenderResponse(). Однако это не true в случае запроса GET к представлению, содержащему тег <f:metadata/> (см. Строку 244 RestoreViewPhase.java , почему это так). Вот почему этот случай выглядит для последующего кода в AccessScopePhaseListener.doAfterRenderResponse() как обратная передача к тому же представлению, что является причиной пропуска аннулирования недоступных bean-компонентов.

Я создал свой собственный слушатель фазы, чтобы исправить эту проблему. Он добавляет к результату AccessScopePhaseListener.doAfterRestoreView() фрагмент, который заставляет состояние атрибута запроса «oldView» выглядеть одинаково для любого запроса GET независимо от того, содержит ли представление <f:metadata/> или нет. Фрагмент запускается до фазы RENDER_RESPONSE, поэтому взаимный порядок слушателя оркестра и мины не важен.

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import org.apache.myfaces.orchestra.conversation.jsf.AccessScopePhaseListener;

public class OrchestraAccessScopeBugFixer implements PhaseListener {
    /**
     * @see AccessScopePhaseListener#OLD_VIEW_KEY
     */
    private static final String OLD_VIEW_KEY = AccessScopePhaseListener.class.getName() + ":oldView";

    @Override
    public void beforePhase(PhaseEvent event) {
        FacesContext facesContext = event.getFacesContext();
        if (!facesContext.isPostback()) {
            // this makes it think that we are on a new view, not posting back to the same one
            facesContext.getExternalContext().getRequestMap().put(OLD_VIEW_KEY, null);
        }
    }

    @Override
    public void afterPhase(PhaseEvent event) {
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RENDER_RESPONSE;
    }
}

Я протестировал это решение для GET для представления с или без <f:metadata/>, для POST для того же представления и для POST с переходом к другому, и оно работает, как и ожидалось. Тем не менее, я не уверен, почему разработчики Orchestra не могли использовать FacesContext.isPostback() вместо FacesContext.getRenderResponse() в AccessScopePhaseListener.doAfterRestoreView(), чтобы сделать разницу между обратной передачей и не одной.

...