001// Copyright 2006-2013 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 java.util.Collections;
018import java.util.Enumeration;
019import java.util.List;
020import java.util.Objects;
021
022import org.apache.tapestry5.commons.util.CollectionFactory;
023import org.apache.tapestry5.http.services.Session;
024import org.apache.tapestry5.ioc.internal.util.InternalUtils;
025
026import javax.servlet.http.HttpServletRequest;
027import javax.servlet.http.HttpSession;
028
029/**
030 * A thin wrapper around {@link HttpSession}.
031 */
032public class SessionImpl implements Session
033{
034    private final HttpServletRequest request;
035
036    private final HttpSession session;
037
038    private boolean invalidated = false;
039
040    private final SessionLock lock;
041
042    public SessionImpl(HttpServletRequest request, HttpSession session, SessionLock lock)
043    {
044        this.request = request;
045        this.session = session;
046        this.lock = lock;
047    }
048
049    @Override
050    public Object getAttribute(String name)
051    {
052        return getAttribute(name, Session.LockMode.WRITE);
053    }
054
055    @Override
056    public Object getAttribute(String name, Session.LockMode lockMode)
057    {
058        Objects.requireNonNull(name, "name must be non-null");
059
060        // If a WRITE lock is requested, check first if the key exists
061        // to prevent a lock upgrade if not necessary.
062        if (lockMode == null || lockMode == Session.LockMode.WRITE)
063        {
064            if (!containsAttribute(name)) return null;
065        }
066
067        acquireLock(lockMode, Session.LockMode.WRITE);
068
069        return session.getAttribute(name);
070    }
071
072    @Override
073    public List<String> getAttributeNames()
074    {
075        return getAttributeNames(Session.LockMode.READ);
076    }
077
078    @Override
079    public List<String> getAttributeNames(Session.LockMode lockMode)
080    {
081        acquireLock(lockMode, Session.LockMode.READ);
082
083        return InternalUtils.toList(session.getAttributeNames());
084    }
085
086    @Override
087    public void setAttribute(String name, Object value)
088    {
089        Objects.requireNonNull(name, "name must be non-null");
090
091        lock.acquireWriteLock();
092
093        session.setAttribute(name, value);
094    }
095
096    @Override
097    public boolean containsAttribute(String name)
098    {
099        return containsAttribute(name, Session.LockMode.READ);
100    }
101
102    @Override
103    public boolean containsAttribute(String name, Session.LockMode lockMode)
104    {
105        Objects.requireNonNull(name, "name must be non-null");
106
107        acquireLock(lockMode, Session.LockMode.READ);
108
109        Enumeration<String> e = session.getAttributeNames();
110        while (e.hasMoreElements())
111        {
112            String attrName = e.nextElement();
113            if (attrName.equals(name)) return true;
114        }
115
116        return false;
117    }
118
119    @Override
120    public List<String> getAttributeNames(String prefix)
121    {
122        return getAttributeNames(prefix, Session.LockMode.READ);
123    }
124
125    @Override
126    public List<String> getAttributeNames(String prefix, Session.LockMode lockMode)
127    {
128        Objects.requireNonNull(prefix, "prefix must be non-null");
129
130        acquireLock(lockMode, Session.LockMode.READ);
131
132        List<String> result = CollectionFactory.newList();
133
134        Enumeration<String> e = session.getAttributeNames();
135        while (e.hasMoreElements())
136        {
137            String name = e.nextElement();
138
139            if (name.startsWith(prefix)) result.add(name);
140        }
141
142        Collections.sort(result);
143
144        return result;
145    }
146
147    @Override
148    public int getMaxInactiveInterval()
149    {
150        return session.getMaxInactiveInterval();
151    }
152
153    @Override
154    public void invalidate()
155    {
156        invalidated = true;
157
158        session.invalidate();
159    }
160
161    @Override
162    public boolean isInvalidated()
163    {
164        if (invalidated) return true;
165
166        // The easy case is when the session was invalidated through the Tapestry Session
167        // object. The hard case is when the HttpSession was invalidated outside of Tapestry,
168        // in which case, request.getSession() will return a new HttpSession instance (or null)
169
170        invalidated = request.getSession(false) != session;
171
172        return invalidated;
173    }
174
175    @Override
176    public void setMaxInactiveInterval(int seconds)
177    {
178        session.setMaxInactiveInterval(seconds);
179    }
180
181    @Override
182    public void restoreDirtyObjects()
183    {
184
185    }
186
187    private void acquireLock(Session.LockMode requestedMode, Session.LockMode defaultMode) {
188        if (requestedMode == null)
189        {
190            requestedMode = defaultMode;
191        }
192
193        switch (requestedMode)
194        {
195            case NONE:
196                break;
197            case READ:
198                this.lock.acquireReadLock();
199                break;
200            case WRITE:
201                this.lock.acquireWriteLock();
202                break;
203        }
204    }
205}