View Javadoc

1   /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.springframework.security.concurrent;
17  
18  import org.springframework.security.ui.session.HttpSessionDestroyedEvent;
19  
20  import org.springframework.context.ApplicationEvent;
21  import org.springframework.context.ApplicationListener;
22  
23  import org.springframework.util.Assert;
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.Date;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Set;
36  
37  import javax.servlet.http.HttpSession;
38  
39  /**
40   * Base implementation of {@link org.springframework.security.concurrent.SessionRegistry}
41   * which also listens for {@link org.springframework.security.ui.session.HttpSessionDestroyedEvent}s
42   * published in the Spring application context.
43   *
44   * <p>
45   * NB: It is important that you register the {@link org.springframework.security.ui.session.HttpSessionEventPublisher} in
46   * <code>web.xml</code> so that this class is notified of sessions that expire.
47   * </p>
48   *
49   * @author Ben Alex
50   * @version $Id: SessionRegistryImpl.java 2550 2008-01-29 16:28:17Z luke_t $
51   */
52  public class SessionRegistryImpl implements SessionRegistry, ApplicationListener {
53      //~ Static fields/initializers =====================================================================================
54  
55      protected static final Log logger = LogFactory.getLog(SessionRegistryImpl.class);
56  
57      // ~ Instance fields ===============================================================================================
58  
59      private Map principals = Collections.synchronizedMap(new HashMap()); // <principal:Object,SessionIdSet>
60      private Map sessionIds = Collections.synchronizedMap(new HashMap()); // <sessionId:Object,SessionInformation>
61  
62      // ~ Methods =======================================================================================================
63  
64      public Object[] getAllPrincipals() {
65          return principals.keySet().toArray();
66      }
67  
68      public SessionInformation[] getAllSessions(Object principal, boolean includeExpiredSessions) {
69          Set sessionsUsedByPrincipal = (Set) principals.get(principal);
70  
71          if (sessionsUsedByPrincipal == null) {
72              return null;
73          }
74  
75          List list = new ArrayList();
76  
77          synchronized (sessionsUsedByPrincipal) {
78              for (Iterator iter = sessionsUsedByPrincipal.iterator(); iter.hasNext();) {
79                  String sessionId = (String) iter.next();
80                  SessionInformation sessionInformation = getSessionInformation(sessionId);
81  
82                  if (sessionInformation == null) {
83                      continue;
84                  }
85  
86                  if (includeExpiredSessions || !sessionInformation.isExpired()) {
87                      list.add(sessionInformation);
88                  }
89              }
90          }
91  
92          return (SessionInformation[]) list.toArray(new SessionInformation[] {});
93      }
94  
95      public SessionInformation getSessionInformation(String sessionId) {
96          Assert.hasText(sessionId, "SessionId required as per interface contract");
97  
98          return (SessionInformation) sessionIds.get(sessionId);
99      }
100 
101     public void onApplicationEvent(ApplicationEvent event) {
102         if (event instanceof HttpSessionDestroyedEvent) {
103             String sessionId = ((HttpSession) event.getSource()).getId();
104             removeSessionInformation(sessionId);
105         }
106     }
107 
108     public void refreshLastRequest(String sessionId) {
109         Assert.hasText(sessionId, "SessionId required as per interface contract");
110 
111         SessionInformation info = getSessionInformation(sessionId);
112 
113         if (info != null) {
114             info.refreshLastRequest();
115         }
116     }
117 
118     public synchronized void registerNewSession(String sessionId, Object principal) {
119         Assert.hasText(sessionId, "SessionId required as per interface contract");
120         Assert.notNull(principal, "Principal required as per interface contract");
121 
122         if (logger.isDebugEnabled()) {
123             logger.debug("Registering session " + sessionId +", for principal " + principal);
124         }
125 
126         if (getSessionInformation(sessionId) != null) {
127             removeSessionInformation(sessionId);
128         }
129 
130         sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date()));
131 
132         Set sessionsUsedByPrincipal = (Set) principals.get(principal);
133 
134         if (sessionsUsedByPrincipal == null) {
135             sessionsUsedByPrincipal = Collections.synchronizedSet(new HashSet(4));
136             principals.put(principal, sessionsUsedByPrincipal);
137         }
138 
139         sessionsUsedByPrincipal.add(sessionId);
140     }
141 
142     public void removeSessionInformation(String sessionId) {
143         Assert.hasText(sessionId, "SessionId required as per interface contract");
144 
145         SessionInformation info = getSessionInformation(sessionId);
146 
147         if (info == null) {
148             return;
149         }
150 
151         if (logger.isDebugEnabled()) {
152             logger.debug("Removing session " + sessionId + " from set of registered sessions");
153         }
154 
155         sessionIds.remove(sessionId);
156 
157         Set sessionsUsedByPrincipal = (Set) principals.get(info.getPrincipal());
158 
159         if (sessionsUsedByPrincipal == null) {
160             return;
161         }
162 
163         if (logger.isDebugEnabled()) {
164             logger.debug("Removing session " + sessionId + " from principal's set of registered sessions");
165         }
166         
167         synchronized (sessionsUsedByPrincipal) {
168             sessionsUsedByPrincipal.remove(sessionId);
169 
170             if (sessionsUsedByPrincipal.size() == 0) {
171                 // No need to keep object in principals Map anymore
172                 if (logger.isDebugEnabled()) {
173                     logger.debug("Removing principal " + info.getPrincipal() + " from registry");
174                 }
175                 principals.remove(info.getPrincipal());
176             }
177         }
178     }
179 }