001//  Copyright 2011, 2013, 2020 The Apache Software Foundation
002//
003//  Licensed under the Apache License, Version 2.0 (the "License");
004//  you may not use this file except in compliance with the License.
005//  You may obtain a copy of the License at
006//
007//  http://www.apache.org/licenses/LICENSE-2.0
008//
009//  Unless required by applicable law or agreed to in writing, software
010//  distributed under the License is distributed on an "AS IS" BASIS,
011//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012//  See the License for the specific language governing permissions and
013//  limitations under the License.
014
015package org.apache.tapestry5.http.internal.services;
016
017import org.apache.tapestry5.commons.util.CollectionFactory;
018import org.apache.tapestry5.http.services.SessionPersistedObjectAnalyzer;
019import org.apache.tapestry5.ioc.services.PerthreadManager;
020
021import javax.servlet.http.HttpServletRequest;
022import javax.servlet.http.HttpSession;
023import java.util.Map;
024
025/**
026 * A thin wrapper around {@link javax.servlet.http.HttpSession} that supports re-storing of mutable
027 * session attributes at the end of the request (see {@link #restoreDirtyObjects()}). This is only
028 * used when {@linkplain org.apache.tapestry5.SymbolConstants#CLUSTERED_SESSIONS clustering}.
029 *
030 * @since 5.3
031 * @see SessionPersistedObjectAnalyzer
032 */
033public class ClusteredSessionImpl extends SessionImpl
034{
035    private final SessionPersistedObjectAnalyzer analyzer;
036
037    /**
038     * Cache of attribute objects read from, or written to, the real session.
039     * This is needed for end-of-request
040     * processing.
041     */
042    private final Map<String, Object> sessionAttributeCache = CollectionFactory.newMap();
043
044    public ClusteredSessionImpl(HttpServletRequest request,
045            HttpSession session,
046            SessionLock lock, SessionPersistedObjectAnalyzer analyzer)
047    {
048        super(request, session, lock);
049
050        this.analyzer = analyzer;
051    }
052
053    @Override
054    public Object getAttribute(String name)
055    {
056        Object result = super.getAttribute(name);
057
058        sessionAttributeCache.put(name, result);
059
060        return result;
061    }
062
063    public void setAttribute(String name, Object value)
064    {
065        super.setAttribute(name, value);
066
067        sessionAttributeCache.put(name, value);
068    }
069
070    public void invalidate()
071    {
072        super.invalidate();
073
074        sessionAttributeCache.clear();
075    }
076
077    public void restoreDirtyObjects()
078    {
079        if (isInvalidated())
080        {
081            return;
082        }
083
084        if (sessionAttributeCache.isEmpty())
085        {
086            return;
087        }
088
089        for (Map.Entry<String, Object> entry : sessionAttributeCache.entrySet())
090        {
091            String attributeName = entry.getKey();
092
093            Object attributeValue = entry.getValue();
094
095            if (attributeValue == null)
096            {
097                continue;
098            }
099
100            if (analyzer.checkAndResetDirtyState(attributeValue))
101            {
102                super.setAttribute(attributeName, attributeValue);
103            }
104        }
105    }
106}