View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions Copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2010–2011 ApexIdentity Inc.
15   * Portions Copyright 2011-2015 ForgeRock AS.
16   */
17  
18  package org.forgerock.http.servlet;
19  
20  import java.io.IOException;
21  import java.util.AbstractMap;
22  import java.util.AbstractSet;
23  import java.util.ArrayList;
24  import java.util.Enumeration;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.NoSuchElementException;
28  import java.util.Set;
29  
30  import jakarta.servlet.http.HttpServletRequest;
31  import jakarta.servlet.http.HttpSession;
32  
33  import org.forgerock.http.session.Session;
34  import org.forgerock.http.protocol.Response;
35  
36  /**
37   * Exposes the session managed by the servlet container as an exchange session.
38   * This implementation will get a servlet session if already allocated,
39   * otherwise will not create one until an attempt is made to put an attribute in
40   * it.
41   */
42  final class ServletSession extends AbstractMap<String, Object> implements Session {
43  
44      /** The servlet request from which to get a servlet session object. */
45      private final HttpServletRequest request;
46  
47      /** The servlet session object, if available. */
48      private volatile HttpSession httpSession;
49  
50      /** The Map entrySet view of the session attributes. */
51      private final Set<Entry<String, Object>> attributes = new AbstractSet<Entry<String, Object>>() {
52          @Override
53          public void clear() {
54              ServletSession.this.clear();
55          }
56  
57          @Override
58          public boolean contains(final Object o) {
59              return (o instanceof Entry)
60                      && ServletSession.this.containsKey(((Entry<?, ?>) o).getKey());
61          }
62  
63          @Override
64          public boolean isEmpty() {
65              return ServletSession.this.isEmpty();
66          }
67  
68          @Override
69          public Iterator<Entry<String, Object>> iterator() {
70              return new Iterator<Entry<String, Object>>() {
71                  final Enumeration<String> names = httpSession != null ? httpSession
72                          .getAttributeNames() : null;
73  
74                  @Override
75                  public boolean hasNext() {
76                      return names != null && names.hasMoreElements();
77                  }
78  
79                  @Override
80                  public Entry<String, Object> next() {
81                      if (names == null) {
82                          throw new NoSuchElementException();
83                      }
84                      final String name = names.nextElement();
85                      return new SimpleEntry<String, Object>(name, httpSession.getAttribute(name)) {
86                          private static final long serialVersionUID = -2957899005221454275L;
87  
88                          @Override
89                          public Object setValue(final Object value) {
90                              put(getKey(), value);
91                              return super.setValue(value);
92                          }
93                      };
94                  }
95  
96                  @Override
97                  public void remove() {
98                      // Enumerations do not support concurrent removals.
99                      throw new UnsupportedOperationException();
100                 }
101             };
102         }
103 
104         @Override
105         public boolean remove(final Object o) {
106             return (o instanceof Entry)
107                     && ServletSession.this.remove(((Entry<?, ?>) o).getKey()) != null;
108         }
109 
110         @Override
111         public int size() {
112             return ServletSession.this.size();
113         }
114     };
115 
116     ServletSession(final HttpServletRequest request) {
117         this.request = request;
118         // get session if already allocated
119         this.httpSession = request.getSession(false);
120     }
121 
122     @Override
123     public void clear() {
124         if (httpSession != null) {
125             // Do in 2 steps to avoid CME.
126             final Enumeration<String> attributes = httpSession.getAttributeNames();
127             final List<String> names = new ArrayList<>();
128             while (attributes.hasMoreElements()) {
129                 names.add(attributes.nextElement());
130             }
131             for (final String name : names) {
132                 httpSession.removeAttribute(name);
133             }
134         }
135     }
136 
137     @Override
138     public boolean containsKey(final Object key) {
139         return get(key) != null;
140     }
141 
142     @Override
143     public Set<Entry<String, Object>> entrySet() {
144         return attributes;
145     }
146 
147     @Override
148     public Object get(final Object key) {
149         Object value = null;
150         if (key instanceof String && httpSession != null) {
151             value = httpSession.getAttribute((String) key);
152         }
153         return value;
154     }
155 
156     @Override
157     public boolean isEmpty() {
158         return httpSession == null || !httpSession.getAttributeNames().hasMoreElements();
159     }
160 
161     @Override
162     public synchronized Object put(final String key, final Object value) {
163         final Object old = get(key);
164         if (httpSession == null) {
165             // create session just-in-time
166             httpSession = request.getSession(true);
167         }
168         httpSession.setAttribute(key, value);
169         return old;
170     }
171 
172     @Override
173     public Object remove(final Object key) {
174         final Object old = get(key);
175         if (key instanceof String && httpSession != null) {
176             httpSession.removeAttribute((String) key);
177         }
178         return old;
179     }
180 
181     @Override
182     public int size() {
183         int size = 0;
184         if (httpSession != null) {
185             final Enumeration<?> attributes = httpSession.getAttributeNames();
186             while (attributes.hasMoreElements()) {
187                 attributes.nextElement();
188                 size++;
189             }
190         }
191         return size;
192     }
193 
194     @Override
195     public void save(Response response) throws IOException {
196         // Nothing to do when using HttpSession
197     }
198 }