Package org.apache.beehive.netui.pageflow.internal

Source Code of org.apache.beehive.netui.pageflow.internal.DeferredSessionStorageHandler$SessionBindingEvent

/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed 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.
*
* $Header:$
*/
package org.apache.beehive.netui.pageflow.internal;

import org.apache.beehive.netui.pageflow.handler.StorageHandler;
import org.apache.beehive.netui.pageflow.RequestContext;
import org.apache.beehive.netui.pageflow.ServletContainerAdapter;
import org.apache.beehive.netui.pageflow.scoping.ScopedServletUtils;
import org.apache.beehive.netui.util.logging.Logger;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Enumeration;
import java.util.ArrayList;
import java.util.Collections;

/**
* This alternate session storage handler does not write any attribute into the session until the very end of a chain
* of forwarded requests (i.e., not even at the end of an inner forwarded request).  This allows it to handle multiple
* concurrent forwarded requests, each of which is modifying the same data, in a more reasonable way.  Basically,
* each request works in its own snapshot of the session, and the last one to commit is the one whose snapshot wins.
* This is a better alternative thatn allowing them to interfere with each other in the middle of the request chain.
*/
public class DeferredSessionStorageHandler
        extends DefaultHandler
        implements StorageHandler
{
    private static final Logger _log = Logger.getInstance(DeferredSessionStorageHandler.class);
   
    private static final String CHANGELIST_ATTR = InternalConstants.ATTR_PREFIX + "changedAttrs";
    private static final String FAILOVER_MAP_ATTR = InternalConstants.ATTR_PREFIX + "failoverAttrs";

    private static ThreadLocal _isCommittingChanges =
            new ThreadLocal()
            {
                public Object initialValue()
                {
                    return Boolean.FALSE;
                }
            };

    public DeferredSessionStorageHandler( ServletContext servletContext )
    {
        init( null, null, servletContext );
    }

    private static final class SessionBindingEvent
        extends HttpSessionBindingEvent
    {
        public SessionBindingEvent( HttpSession httpSession, String attrName )
        {
            super( httpSession, attrName );
        }

        public SessionBindingEvent( HttpSession httpSession, String attrName, Object attrVal )
        {
            super( httpSession, attrName, attrVal );
        }
    }

    public void setAttribute( RequestContext context, String attrName, Object value )
    {
        if (_log.isTraceEnabled()) {
            _log.trace("setAttribute: " + attrName + "=" + value);
        }
       
        HttpServletRequest request = ScopedServletUtils.getOuterRequest( ( HttpServletRequest ) context.getRequest() );
        Object currentValue = request.getAttribute( attrName );

        //
        // Note that we unconditionally get/create the session.  We want to make sure that the session exists when
        // we set an attribute, since we will *not* create it in applyChanges, to prevent recreation of an
        // intentionally-invalidated session.
        //
        HttpSession session = request.getSession();

        //
        // Emulate a setAttribute on the session: if the value is an HttpSessionBindingListener, invoke its
        // valueUnbound().  Note that we don't currently care about calling valueBound().
        //
        if ( currentValue != null && currentValue != value && currentValue instanceof HttpSessionBindingListener )
        {
            HttpSessionBindingEvent event = new SessionBindingEvent( session, attrName, currentValue );
            ( ( HttpSessionBindingListener ) currentValue ).valueUnbound( event );
        }

        request.setAttribute( attrName, value );
        getChangedAttributesList( request, true, false ).add( attrName );

        HashMap failoverAttrs = getFailoverAttributesMap( request, false, false );
        if ( failoverAttrs != null ) failoverAttrs.remove( attrName );
    }

    public void removeAttribute( RequestContext context, String attrName )
    {
        if (_log.isTraceEnabled()) {
            _log.trace("removeAttribute: " + attrName);
        }
       
        HttpServletRequest request = ScopedServletUtils.getOuterRequest( ( HttpServletRequest ) context.getRequest() );
        Object currentValue = request.getAttribute( attrName );

        //
        // Emulate a removeAttribute on the session: if the value is an HttpSessionBindingListener, invoke its
        // valueUnbound().
        //
        if ( currentValue != null && currentValue instanceof HttpSessionBindingListener )
        {
            HttpSessionBindingEvent event = new SessionBindingEvent( request.getSession(), attrName, currentValue );
            ( ( HttpSessionBindingListener ) currentValue ).valueUnbound( event );
        }

        request.removeAttribute( attrName );
        getChangedAttributesList( request, true, false ).add( attrName );

        HashMap failoverAttrs = getFailoverAttributesMap( request, false, false );
        if ( failoverAttrs != null ) failoverAttrs.remove( attrName );
    }

    public Object getAttribute( RequestContext context, String attributeName )
    {
        if (_log.isTraceEnabled()) {
            _log.trace("getAttribute: " + attributeName);
        }
       
        HttpServletRequest request = ScopedServletUtils.getOuterRequest( ( HttpServletRequest ) context.getRequest() );
        Object val = request.getAttribute( attributeName );
        if ( val != null ) return val;

        // If the attribute isn't present in the request and is in the list of changed attrs, then it was removed.
        // Don't fall back to the session attribute in that case.
        HashSet changedAttrs = getChangedAttributesList( request, false, false );
        if ( changedAttrs != null && changedAttrs.contains( attributeName ) ) return null;


        // Get the attribute out of the session, and put it into the request.  Until applyChanges is called, this is
        // the value we'll use.
        HttpSession session = request.getSession( false );
        if ( session != null )
        {
            val = session.getAttribute( attributeName );
            if ( val != null ) request.setAttribute( attributeName, val );
        }

        return val;
    }

    public void applyChanges( RequestContext context )
    {
        HttpServletRequest request = ScopedServletUtils.getOuterRequest( ( HttpServletRequest ) context.getRequest() );
        HttpSession session = request.getSession( false );

        if (_log.isDebugEnabled()) {
            _log.debug("Applying changes for request " + request.getRequestURI());
        }
       
        //
        // If the session doesn't exist, we don't want to recreate it.  It has most likely been intentionally
        // invalidated, since we did ensure its existance in getAttribute.
        //
        if ( session == null ) return;

        HashSet changedAttrs = getChangedAttributesList( request, false, true );

        if ( changedAttrs != null )
        {
            //
            // Go through each changed attribute, and either write it to the session or remove it to the session,
            // depending on whether or not it exists in the request.
            //
            for ( Iterator i = changedAttrs.iterator(); i.hasNext(); )
            {
                String attrName = ( String ) i.next();
                Object val = request.getAttribute( attrName );

                if ( val != null )
                {
                    // Write it to the session, but only if the current value isn't already this value.
                    Object currentValue = session.getAttribute( attrName );

                    if ( currentValue != val )
                    {
                        if (_log.isTraceEnabled()) {
                            _log.trace("Committing attribute " + attrName + " to the session.");
                        }
                       
                        // This ThreadLocal value allows others (e.g., an HttpSessionBindingListener like
                        // PageFlowManagedObject) that we're in the middle of committing changes to the session.
                        _isCommittingChanges.set( Boolean.TRUE );

                        try
                        {
                            session.setAttribute( attrName, val );
                        }
                        finally
                        {
                            _isCommittingChanges.set( Boolean.FALSE );
                        }

                        request.removeAttribute( attrName );
                    }
                }
                else
                {
                    if (_log.isTraceEnabled()) {
                        _log.trace("Removing attribute " + attrName + " from the session.");
                    }
                       
                    //
                    // This ThreadLocal value allows others (e.g., an HttpSessionBindingListener like
                    // PageFlowManagedObject) that we're in the middle of committing changes to the session.
                    //
                    _isCommittingChanges.set( Boolean.TRUE );

                    try
                    {
                        session.removeAttribute( attrName );
                    }
                    finally
                    {
                        _isCommittingChanges.set( Boolean.FALSE );
                    }
                }
            }
        }


        //
        // Now go through the attributes we need to ensure-failover on.
        //
        HashMap failoverAttrs = getFailoverAttributesMap( request, false, true );

        if ( failoverAttrs != null )
        {
            ServletContainerAdapter sa = AdapterManager.getServletContainerAdapter( getServletContext() );

            for ( Iterator i = failoverAttrs.entrySet().iterator(); i.hasNext(); )
            {
                Map.Entry entry = ( Map.Entry ) i.next();
                if (_log.isTraceEnabled()) {
                    _log.trace("Ensure failover for attribute " + entry.getKey());
                }
                sa.ensureFailover( ( String ) entry.getKey(), entry.getValue(), request );
            }
        }
    }

    public void dropChanges(RequestContext context)
    {
        HttpServletRequest request = ScopedServletUtils.getOuterRequest( ( HttpServletRequest ) context.getRequest() );
        if (_log.isDebugEnabled()) {
            _log.debug("Dropping changes for request " + request.getRequestURI());
        }
        getChangedAttributesList( request, false, true );
        getFailoverAttributesMap( request, false, true );
    }

    public Enumeration getAttributeNames(RequestContext context) {
        HttpServletRequest request = ScopedServletUtils.getOuterRequest( ( HttpServletRequest ) context.getRequest() );
        HttpSession session = request.getSession( false );
        ArrayList attrNames = new ArrayList();

        // Start with the attribute names that are in the session.
        if (session != null) {
            for (Enumeration e = session.getAttributeNames(); e.hasMoreElements(); ) {
                attrNames.add((String) e.nextElement());
            }
        }

        // Add or remove as necessary, based on the list of changed attributes.
        HashSet changedAttrs = getChangedAttributesList( request, false, false );
        if (changedAttrs != null) {
            for (Iterator i = changedAttrs.iterator(); i.hasNext(); ) {
                String attrName = (String) i.next();

                if (request.getAttribute(attrName) != null) {
                    attrNames.add(attrName);
                } else {
                    // If the attribute isn't present in the request and is in the session, then it's scheduled for
                    // removal.
                    attrNames.remove(attrName);
                }
            }
        }

        return Collections.enumeration(attrNames);
    }

    public void ensureFailover( RequestContext context, String attributeName, Object value )
    {
        HttpServletRequest request = ScopedServletUtils.getOuterRequest( ( HttpServletRequest ) context.getRequest() );
        getFailoverAttributesMap( request, true, false ).put( attributeName, value );
    }

    private static HashSet getChangedAttributesList( ServletRequest request, boolean create, boolean remove )
    {
        HashSet set = ( HashSet ) request.getAttribute( CHANGELIST_ATTR );

        if ( set == null && create )
        {
            set = new HashSet();
            request.setAttribute( CHANGELIST_ATTR, set );
        }

        if ( set!= null && remove ) request.removeAttribute( CHANGELIST_ATTR );

        return set;
    }

    private static HashMap getFailoverAttributesMap( ServletRequest request, boolean create, boolean remove )
    {
        HashMap map = ( HashMap ) request.getAttribute( FAILOVER_MAP_ATTR );

        if ( map == null && create )
        {
            map = new HashMap();
            request.setAttribute( FAILOVER_MAP_ATTR, map );
        }

        if ( map != null && remove ) request.removeAttribute( FAILOVER_MAP_ATTR );

        return map;
    }

    public boolean allowBindingEvent( Object event )
    {
        return ! ( ( Boolean ) _isCommittingChanges.get() ).booleanValue();
    }
}
TOP

Related Classes of org.apache.beehive.netui.pageflow.internal.DeferredSessionStorageHandler$SessionBindingEvent

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.