Package org.apache.myfaces.orchestra.conversation.jsf

Source Code of org.apache.myfaces.orchestra.conversation.jsf.AccessScopePhaseListener

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.myfaces.orchestra.conversation.jsf;

import java.util.Iterator;
import java.util.Set;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.orchestra.conversation.AccessScopeManager;
import org.apache.myfaces.orchestra.conversation.Conversation;
import org.apache.myfaces.orchestra.conversation.ConversationAccessLifetimeAspect;
import org.apache.myfaces.orchestra.conversation.ConversationManager;

/**
* Handle access-scoped conversations.
* <p>
* At the end of request processing, delete any access-scope conversations for which no
* bean in that scope has been accessed during the <i>render phase</i> of the request.
* Therefore if a postback occurs which accesses a specific conversation A, and that
* conversation then forwards to a different view whose render phase does not refer
* to any bean in conversation A, then the conversation is discarded. This ensures
* that conversations are discarded as soon as possible, and in particular that if
* control returns to the a view that accesses conversation A on the next cycle then
* a new conversation instance is created.
* <p>
* If a view happens to want its postbacks handled by a bean in conversation A,
* but the render phase never references anything in that conversation, then the
* conversation will be effectively request-scoped. This is not expected to be a
* problem in practice as it would be a pretty odd view that has stateful event
* handling but either renders nothing or fetches its data from somewhere other
* than the same conversation. If such a case is necessary, the view can be modified
* to "ping" the conversation in order to keep it active via something like an
* h:outputText with rendered="#{backingBean.class is null}" (which always resolves
* to false, ie not rendered, but does force a method-invocation on the backingBean
* instance). Alternatively, a manual-scoped conversation can be used.
* <p>
* Note that if FacesContext.responseComplete is called during processing, then
* no phase-listeners for the RENDER_RESPONSE phase are executed. And any navigation
* rule that specifies "redirect" causes responseComplete to be invoked. Therefore
* access-scoped beans are not cleaned up immediately. However the view being
* redirected to always runs its "render" phase only, no postback. The effect,
* therefore, is exactly the same as when an internal forward is performed to
* the same view: in both cases, the access-scoped beans are kept if the next view
* refers to them, and discarded otherwise.
* <p>
* Note also that if responseComplete is invoked by another PhaseListener in the
* beforePhase for RENDER_RESPONSE then the beforePhase for all other PhaseListeners
* is still invoked, but no afterPhase methods are invoked. This means that this
* phase listener will not discard any access-scoped conversations, as that is
* only done in the afterPhase method. In particular, this applies to tomahawk
* PPR requests, where the PPRPhaseListener uses responseComplete(). This behaviour
* is exactly what is wanted; partial-page-rendering should not cause access-scoped
* conversations to terminate.
*
* @since 1.1
*/
public class AccessScopePhaseListener implements PhaseListener
{
    private static final long serialVersionUID = 1L;
    private final Log log = LogFactory.getLog(AccessScopePhaseListener.class);

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

    public void beforePhase(PhaseEvent event)
    {
        AccessScopeManager accessManager = AccessScopeManager.getInstance();
        accessManager.beginRecording();
    }

    public void afterPhase(PhaseEvent event)
    {
        invalidateAccessScopedConversations(event.getFacesContext().getViewRoot().getViewId());
    }

    /**
     * Invalidates any conversation with aspect {@link ConversationAccessLifetimeAspect}
     * which has not been accessed during a http request
     */
    protected void invalidateAccessScopedConversations(String viewId)
    {
        AccessScopeManager accessManager = AccessScopeManager.getInstance();
        if (accessManager.isIgnoreRequest())
        {
            return;
        }

        if (accessManager.getAccessScopeManagerConfiguration() != null)
        {
            Set ignoredViewIds = accessManager.getAccessScopeManagerConfiguration().getIgnoreViewIds();
            if (ignoredViewIds != null && ignoredViewIds.contains(viewId))
            {
                // The scope configuration has explicitly stated that no conversations should be
                // terminated when processing this specific view, so just return.
                //
                // Special "ignored views" are useful when dealing with things like nested
                // frames within a page that periodically refresh themselves while the "main"
                // part of the page remains unsubmitted.
                return;
            }
        }

        ConversationManager conversationManager = ConversationManager.getInstance(false);
        if (conversationManager == null)
        {
            return;
        }

        boolean isDebug = log.isDebugEnabled();
        Iterator iterConversations = conversationManager.iterateConversations();
        while (iterConversations.hasNext())
        {
            Conversation conversation = (Conversation) iterConversations.next();
           
            // This conversation has "access" scope if it has an attached Aspect
            // of type ConversationAccessLifetimeAspect. All other conversations
            // are not access-scoped and should be ignored here.
            ConversationAccessLifetimeAspect aspect =
                (ConversationAccessLifetimeAspect)
                    conversation.getAspect(ConversationAccessLifetimeAspect.class);

            if (aspect != null)
            {
                if (aspect.isAccessed())
                {
                    if (isDebug)
                    {
                        log.debug(
                            "Not clearing accessed conversation " + conversation.getName()
                            + " after rendering view " + viewId);
                    }
                }
                else
                {
                    if (isDebug)
                    {
                        log.debug(
                            "Clearing access-scoped conversation " + conversation.getName()
                            + " after rendering view " + viewId);
                    }
                    conversation.invalidate();
                }
            }
        }
    }
}
TOP

Related Classes of org.apache.myfaces.orchestra.conversation.jsf.AccessScopePhaseListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.