001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.web.session;
017
018import com.google.common.collect.Maps;
019import com.google.common.collect.Sets;
020
021import javax.servlet.ServletContext;
022import javax.servlet.http.HttpSession;
023import javax.servlet.http.HttpSessionContext;
024import java.util.*;
025
026public class JbootHttpSession implements HttpSession {
027
028    private final String id;
029
030    private final long createdAt;
031
032    private volatile long lastAccessedAt;
033
034    private int maxInactiveInterval;
035
036    private final ServletContext servletContext;
037
038    private final Map<String, Object> newAttributes = Maps.newHashMap();
039    private final Set<String> deleteAttribute = Sets.newHashSet();
040    private final Map<String, Object> sessionStore;
041
042    private volatile boolean invalid = false;
043    private volatile boolean dataChanged = false;
044    private volatile boolean empty = false;
045
046    private volatile HttpSession originSession;
047
048    public JbootHttpSession(String id, ServletContext servletContext, Map<String, Object> sessionStore, HttpSession originSession) {
049        this.id = id;
050        this.servletContext = servletContext;
051        this.sessionStore = sessionStore;
052        this.createdAt = System.currentTimeMillis();
053        this.lastAccessedAt = createdAt;
054        this.empty = sessionStore.isEmpty();
055        this.originSession = originSession;
056    }
057
058
059
060    @Override
061    public long getCreationTime() {
062        return createdAt;
063    }
064
065    @Override
066    public String getId() {
067        return id;
068    }
069
070    @Override
071    public long getLastAccessedTime() {
072        return lastAccessedAt;
073    }
074
075    @Override
076    public ServletContext getServletContext() {
077        return servletContext;
078    }
079
080    @Override
081    public void setMaxInactiveInterval(int interval) {
082        this.maxInactiveInterval = interval;
083    }
084
085    @Override
086    public int getMaxInactiveInterval() {
087        return maxInactiveInterval;
088    }
089
090    @Override
091    @Deprecated
092    public HttpSessionContext getSessionContext() {
093        return null;
094    }
095
096    @Override
097    public Object getAttribute(String name) {
098        checkValid();
099        if (newAttributes.containsKey(name)) {
100            return newAttributes.get(name);
101        } else if (deleteAttribute.contains(name)) {
102            return null;
103        }
104        return sessionStore.get(name);
105    }
106
107    @Override
108    public Object getValue(String name) {
109        return getAttribute(name);
110    }
111
112    @Override
113    public Enumeration<String> getAttributeNames() {
114        checkValid();
115        Set<String> names = Sets.newHashSet(sessionStore.keySet());
116        names.addAll(newAttributes.keySet());
117        names.removeAll(deleteAttribute);
118        return Collections.enumeration(names);
119    }
120
121    @Override
122    public String[] getValueNames() {
123        checkValid();
124        Set<String> names = Sets.newHashSet(sessionStore.keySet());
125        names.addAll(newAttributes.keySet());
126        names.removeAll(deleteAttribute);
127        return names.toArray(new String[0]);
128    }
129
130    @Override
131    public void setAttribute(String name, Object value) {
132        checkValid();
133        if (value == null) {
134            removeAttribute(name);
135            return;
136        }
137
138        newAttributes.put(name, value);
139        deleteAttribute.remove(name);
140        empty = false;
141        dataChanged = true;
142
143        if (originSession != null) {
144            originSession.setAttribute(name, value);
145        }
146
147    }
148
149    @Override
150    public void putValue(String name, Object value) {
151        setAttribute(name, value);
152    }
153
154    @Override
155    public void removeAttribute(String name) {
156        checkValid();
157        if (empty && newAttributes.isEmpty()) {
158            return;
159        }
160
161        if (!newAttributes.containsKey(name) && !sessionStore.containsKey(name)) {
162            return;
163        }
164
165        deleteAttribute.add(name);
166        newAttributes.remove(name);
167        dataChanged = true;
168
169        if (originSession != null) {
170            originSession.removeAttribute(name);
171        }
172    }
173
174    @Override
175    public void removeValue(String name) {
176        removeAttribute(name);
177    }
178
179    @Override
180    public void invalidate() {
181        invalid = true;
182        dataChanged = true;
183
184        if (originSession != null) {
185            originSession.invalidate();
186        }
187    }
188
189    @Override
190    public boolean isNew() {
191        return Boolean.TRUE;
192    }
193
194
195    public boolean isDataChanged() {
196        return dataChanged;
197    }
198
199
200    public Map<String, Object> snapshot() {
201        Map<String, Object> snap = new HashMap<>();
202        snap.putAll(sessionStore);
203        snap.putAll(newAttributes);
204        for (String name : deleteAttribute) {
205            snap.remove(name);
206        }
207        return snap;
208    }
209
210    public boolean isValid() {
211        return !invalid;
212    }
213
214    protected void checkValid() throws IllegalStateException {
215        if (invalid) {
216            throw new IllegalStateException("http session has invalidate");
217        }
218    }
219
220    public boolean isEmpty() {
221        return empty;
222    }
223}