SEC-484: fixed concurrency issue

This commit is contained in:
Vishal Puri 2007-07-23 07:58:31 +00:00
parent e70f01c260
commit 5ea8232f84
2 changed files with 203 additions and 178 deletions

View File

@ -34,7 +34,6 @@ import java.util.Set;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
/** /**
* Base implementation of {@link org.acegisecurity.concurrent.SessionRegistry} * Base implementation of {@link org.acegisecurity.concurrent.SessionRegistry}
* which also listens for {@link * which also listens for {@link
@ -47,113 +46,123 @@ import javax.servlet.http.HttpSession;
* <code>web.xml</code> so that this class is notified of sessions that * <code>web.xml</code> so that this class is notified of sessions that
* expire. * expire.
* </p> * </p>
* *
* @author Ben Alex * @author Ben Alex
* @version $Id${date} * @version $Id: SessionRegistryImpl.java 1862 2007-05-25 01:38:42Z benalex
* ${date}
*/ */
public class SessionRegistryImpl implements SessionRegistry, public class SessionRegistryImpl implements SessionRegistry,
ApplicationListener { ApplicationListener {
//~ Instance fields ======================================================== // ~ Instance fields
// ========================================================
private Map principals = Collections.synchronizedMap(new HashMap()); // <principal:Object,SessionIdSet> private Map principals = Collections.synchronizedMap(new HashMap()); // <principal:Object,SessionIdSet>
private Map sessionIds = Collections.synchronizedMap(new HashMap()); // <sessionId:Object,SessionInformation> private Map sessionIds = Collections.synchronizedMap(new HashMap()); // <sessionId:Object,SessionInformation>
//~ Methods ================================================================ // ~ Methods
// ================================================================
public Object[] getAllPrincipals() { public Object[] getAllPrincipals() {
return principals.keySet().toArray(); return principals.keySet().toArray();
} }
public SessionInformation[] getAllSessions(Object principal, public SessionInformation[] getAllSessions(Object principal,
boolean includeExpiredSessions) { boolean includeExpiredSessions) {
Set sessionsUsedByPrincipal = (Set) principals.get(principal); Set sessionsUsedByPrincipal = (Set) principals.get(principal);
if (sessionsUsedByPrincipal == null) {
return null;
}
if (sessionsUsedByPrincipal == null) { List list = new ArrayList();
return null;
}
List list = new ArrayList(); synchronized (sessionsUsedByPrincipal) {
Iterator iter = sessionsUsedByPrincipal.iterator(); for (Iterator iter = sessionsUsedByPrincipal.iterator(); iter
.hasNext();) {
String sessionId = (String) iter.next();
SessionInformation sessionInformation = getSessionInformation(sessionId);
while (iter.hasNext()) { if (includeExpiredSessions || !sessionInformation.isExpired()) {
synchronized (sessionsUsedByPrincipal) { list.add(sessionInformation);
String sessionId = (String) iter.next(); }
SessionInformation sessionInformation = getSessionInformation(sessionId); }
}
if (includeExpiredSessions || !sessionInformation.isExpired()) { return (SessionInformation[]) list.toArray(new SessionInformation[] {});
list.add(sessionInformation); }
}
}
}
return (SessionInformation[]) list.toArray(new SessionInformation[] {}); public SessionInformation getSessionInformation(String sessionId) {
} Assert.hasText(sessionId,
"SessionId required as per interface contract");
public SessionInformation getSessionInformation(String sessionId) { return (SessionInformation) sessionIds.get(sessionId);
Assert.hasText(sessionId, "SessionId required as per interface contract"); }
return (SessionInformation) sessionIds.get(sessionId); public void onApplicationEvent(ApplicationEvent event) {
} if (event instanceof HttpSessionDestroyedEvent) {
String sessionId = ((HttpSession) event.getSource()).getId();
removeSessionInformation(sessionId);
}
}
public void onApplicationEvent(ApplicationEvent event) { public void refreshLastRequest(String sessionId) {
if (event instanceof HttpSessionDestroyedEvent) { Assert.hasText(sessionId,
String sessionId = ((HttpSession) event.getSource()).getId(); "SessionId required as per interface contract");
removeSessionInformation(sessionId);
}
}
public void refreshLastRequest(String sessionId) { SessionInformation info = getSessionInformation(sessionId);
Assert.hasText(sessionId, "SessionId required as per interface contract");
SessionInformation info = getSessionInformation(sessionId); if (info != null) {
info.refreshLastRequest();
}
}
if (info != null) { public synchronized void registerNewSession(String sessionId,
info.refreshLastRequest(); Object principal) {
} Assert.hasText(sessionId,
} "SessionId required as per interface contract");
Assert.notNull(principal,
"Principal required as per interface contract");
public synchronized void registerNewSession(String sessionId, Object principal) { if (getSessionInformation(sessionId) != null) {
Assert.hasText(sessionId, "SessionId required as per interface contract"); removeSessionInformation(sessionId);
Assert.notNull(principal, "Principal required as per interface contract"); }
if (getSessionInformation(sessionId) != null) { sessionIds.put(sessionId, new SessionInformation(principal, sessionId,
removeSessionInformation(sessionId); new Date()));
}
sessionIds.put(sessionId, Set sessionsUsedByPrincipal = (Set) principals.get(principal);
new SessionInformation(principal, sessionId, new Date()));
Set sessionsUsedByPrincipal = (Set) principals.get(principal); if (sessionsUsedByPrincipal == null) {
sessionsUsedByPrincipal = Collections
.synchronizedSet(new HashSet());
}
if (sessionsUsedByPrincipal == null) { sessionsUsedByPrincipal.add(sessionId);
sessionsUsedByPrincipal = Collections.synchronizedSet(new HashSet());
}
sessionsUsedByPrincipal.add(sessionId); principals.put(principal, sessionsUsedByPrincipal);
}
principals.put(principal, sessionsUsedByPrincipal); public void removeSessionInformation(String sessionId) {
} Assert.hasText(sessionId,
"SessionId required as per interface contract");
public void removeSessionInformation(String sessionId) { SessionInformation info = getSessionInformation(sessionId);
Assert.hasText(sessionId, "SessionId required as per interface contract");
SessionInformation info = getSessionInformation(sessionId); if (info != null) {
sessionIds.remove(sessionId);
if (info != null) { Set sessionsUsedByPrincipal = (Set) principals.get(info
sessionIds.remove(sessionId); .getPrincipal());
Set sessionsUsedByPrincipal = (Set) principals.get(info.getPrincipal()); if (sessionsUsedByPrincipal != null) {
synchronized (sessionsUsedByPrincipal) {
sessionsUsedByPrincipal.remove(sessionId);
if (sessionsUsedByPrincipal != null) { if (sessionsUsedByPrincipal.size() == 0) {
synchronized (sessionsUsedByPrincipal) { // No need to keep object in principals Map anymore
sessionsUsedByPrincipal.remove(sessionId); principals.remove(info.getPrincipal());
}
if (sessionsUsedByPrincipal.size() == 0) { }
// No need to keep object in principals Map anymore }
principals.remove(info.getPrincipal()); }
} }
}
}
}
}
} }

View File

@ -23,137 +23,153 @@ import org.springframework.mock.web.MockHttpSession;
import java.util.Date; import java.util.Date;
/** /**
* Tests {@link SessionRegistryImpl}. * Tests {@link SessionRegistryImpl}.
* *
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
*/ */
public class SessionRegistryImplTests extends TestCase { public class SessionRegistryImplTests extends TestCase {
//~ Methods ======================================================================================================== // ~ Methods
// ========================================================================================================
public void testEventPublishing() { public void testEventPublishing() {
MockHttpSession httpSession = new MockHttpSession(); MockHttpSession httpSession = new MockHttpSession();
Object principal = "Some principal object"; Object principal = "Some principal object";
String sessionId = httpSession.getId(); String sessionId = httpSession.getId();
assertNotNull(sessionId); assertNotNull(sessionId);
SessionRegistryImpl sessionRegistry = new SessionRegistryImpl(); SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
// Register new Session // Register new Session
sessionRegistry.registerNewSession(sessionId, principal); sessionRegistry.registerNewSession(sessionId, principal);
// Deregister session via an ApplicationEvent // Deregister session via an ApplicationEvent
sessionRegistry.onApplicationEvent(new HttpSessionDestroyedEvent(httpSession)); sessionRegistry.onApplicationEvent(new HttpSessionDestroyedEvent(
httpSession));
// Check attempts to retrieve cleared session return null // Check attempts to retrieve cleared session return null
assertNull(sessionRegistry.getSessionInformation(sessionId)); assertNull(sessionRegistry.getSessionInformation(sessionId));
} }
public void testMultiplePrincipals() throws Exception { public void testMultiplePrincipals() throws Exception {
Object principal1 = "principal_1"; Object principal1 = "principal_1";
Object principal2 = "principal_2"; Object principal2 = "principal_2";
String sessionId1 = "1234567890"; String sessionId1 = "1234567890";
String sessionId2 = "9876543210"; String sessionId2 = "9876543210";
String sessionId3 = "5432109876"; String sessionId3 = "5432109876";
SessionRegistryImpl sessionRegistry = new SessionRegistryImpl(); SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
sessionRegistry.registerNewSession(sessionId1, principal1); sessionRegistry.registerNewSession(sessionId1, principal1);
sessionRegistry.registerNewSession(sessionId2, principal1); sessionRegistry.registerNewSession(sessionId2, principal1);
sessionRegistry.registerNewSession(sessionId3, principal2); sessionRegistry.registerNewSession(sessionId3, principal2);
assertEquals(principal1, sessionRegistry.getAllPrincipals()[0]); assertEquals(principal1, sessionRegistry.getAllPrincipals()[0]);
assertEquals(principal2, sessionRegistry.getAllPrincipals()[1]); assertEquals(principal2, sessionRegistry.getAllPrincipals()[1]);
} }
public void testSessionInformationLifecycle() throws Exception { public void testSessionInformationLifecycle() throws Exception {
Object principal = "Some principal object"; Object principal = "Some principal object";
String sessionId = "1234567890"; String sessionId = "1234567890";
SessionRegistryImpl sessionRegistry = new SessionRegistryImpl(); SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
// Register new Session // Register new Session
sessionRegistry.registerNewSession(sessionId, principal); sessionRegistry.registerNewSession(sessionId, principal);
// Retrieve existing session by session ID // Retrieve existing session by session ID
Date currentDateTime = sessionRegistry.getSessionInformation(sessionId).getLastRequest(); Date currentDateTime = sessionRegistry.getSessionInformation(sessionId)
assertEquals(principal, sessionRegistry.getSessionInformation(sessionId).getPrincipal()); .getLastRequest();
assertEquals(sessionId, sessionRegistry.getSessionInformation(sessionId).getSessionId()); assertEquals(principal, sessionRegistry
assertNotNull(sessionRegistry.getSessionInformation(sessionId).getLastRequest()); .getSessionInformation(sessionId).getPrincipal());
assertEquals(sessionId, sessionRegistry
.getSessionInformation(sessionId).getSessionId());
assertNotNull(sessionRegistry.getSessionInformation(sessionId)
.getLastRequest());
// Retrieve existing session by principal // Retrieve existing session by principal
assertEquals(1, sessionRegistry.getAllSessions(principal, false).length); assertEquals(1, sessionRegistry.getAllSessions(principal, false).length);
// Sleep to ensure SessionRegistryImpl will update time // Sleep to ensure SessionRegistryImpl will update time
Thread.sleep(1000); Thread.sleep(1000);
// Update request date/time // Update request date/time
sessionRegistry.refreshLastRequest(sessionId); sessionRegistry.refreshLastRequest(sessionId);
Date retrieved = sessionRegistry.getSessionInformation(sessionId).getLastRequest(); Date retrieved = sessionRegistry.getSessionInformation(sessionId)
assertTrue(retrieved.after(currentDateTime)); .getLastRequest();
assertTrue(retrieved.after(currentDateTime));
// Check it retrieves correctly when looked up via principal // Check it retrieves correctly when looked up via principal
assertEquals(retrieved, sessionRegistry.getAllSessions(principal, false)[0].getLastRequest()); assertEquals(retrieved, sessionRegistry
.getAllSessions(principal, false)[0].getLastRequest());
// Clear session information // Clear session information
sessionRegistry.removeSessionInformation(sessionId); sessionRegistry.removeSessionInformation(sessionId);
// Check attempts to retrieve cleared session return null // Check attempts to retrieve cleared session return null
assertNull(sessionRegistry.getSessionInformation(sessionId)); assertNull(sessionRegistry.getSessionInformation(sessionId));
assertNull(sessionRegistry.getAllSessions(principal, false)); assertNull(sessionRegistry.getAllSessions(principal, false));
} }
public void testTwoSessionsOnePrincipalExpiring() throws Exception { public void testTwoSessionsOnePrincipalExpiring() throws Exception {
Object principal = "Some principal object"; Object principal = "Some principal object";
String sessionId1 = "1234567890"; String sessionId1 = "1234567890";
String sessionId2 = "9876543210"; String sessionId2 = "9876543210";
SessionRegistryImpl sessionRegistry = new SessionRegistryImpl(); SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
// Register new Session // Register new Session
sessionRegistry.registerNewSession(sessionId1, principal); sessionRegistry.registerNewSession(sessionId1, principal);
assertEquals(1, sessionRegistry.getAllSessions(principal, false).length); assertEquals(1, sessionRegistry.getAllSessions(principal, false).length);
assertEquals(sessionId1, sessionRegistry.getAllSessions(principal, false)[0].getSessionId()); assertEquals(sessionId1, sessionRegistry.getAllSessions(principal,
false)[0].getSessionId());
// Register new Session // Register new Session
sessionRegistry.registerNewSession(sessionId2, principal); sessionRegistry.registerNewSession(sessionId2, principal);
assertEquals(2, sessionRegistry.getAllSessions(principal, false).length); assertEquals(2, sessionRegistry.getAllSessions(principal, false).length);
assertEquals(sessionId2, sessionRegistry.getAllSessions(principal, false)[1].getSessionId()); assertEquals(sessionId2, sessionRegistry.getAllSessions(principal,
false)[1].getSessionId());
// Expire one session // Expire one session
SessionInformation session = sessionRegistry.getSessionInformation(sessionId2); SessionInformation session = sessionRegistry
session.expireNow(); .getSessionInformation(sessionId2);
session.expireNow();
// Check retrieval still correct // Check retrieval still correct
assertTrue(sessionRegistry.getSessionInformation(sessionId2).isExpired()); assertTrue(sessionRegistry.getSessionInformation(sessionId2)
assertFalse(sessionRegistry.getSessionInformation(sessionId1).isExpired()); .isExpired());
} assertFalse(sessionRegistry.getSessionInformation(sessionId1)
.isExpired());
}
public void testTwoSessionsOnePrincipalHandling() throws Exception { public void testTwoSessionsOnePrincipalHandling() throws Exception {
Object principal = "Some principal object"; Object principal = "Some principal object";
String sessionId1 = "1234567890"; String sessionId1 = "1234567890";
String sessionId2 = "9876543210"; String sessionId2 = "9876543210";
SessionRegistryImpl sessionRegistry = new SessionRegistryImpl(); SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
// Register new Session // Register new Session
sessionRegistry.registerNewSession(sessionId1, principal); sessionRegistry.registerNewSession(sessionId1, principal);
assertEquals(1, sessionRegistry.getAllSessions(principal, false).length); assertEquals(1, sessionRegistry.getAllSessions(principal, false).length);
assertEquals(sessionId1, sessionRegistry.getAllSessions(principal, false)[0].getSessionId()); assertEquals(sessionId1, sessionRegistry.getAllSessions(principal,
false)[0].getSessionId());
// Register new Session // Register new Session
sessionRegistry.registerNewSession(sessionId2, principal); sessionRegistry.registerNewSession(sessionId2, principal);
assertEquals(2, sessionRegistry.getAllSessions(principal, false).length); assertEquals(2, sessionRegistry.getAllSessions(principal, false).length);
assertEquals(sessionId2, sessionRegistry.getAllSessions(principal, false)[1].getSessionId()); assertEquals(sessionId2, sessionRegistry.getAllSessions(principal,
false)[1].getSessionId());
// Clear session information // Clear session information
sessionRegistry.removeSessionInformation(sessionId1); sessionRegistry.removeSessionInformation(sessionId1);
assertEquals(1, sessionRegistry.getAllSessions(principal, false).length); assertEquals(1, sessionRegistry.getAllSessions(principal, false).length);
assertEquals(sessionId2, sessionRegistry.getAllSessions(principal, false)[0].getSessionId()); assertEquals(sessionId2, sessionRegistry.getAllSessions(principal,
false)[0].getSessionId());
// Clear final session
sessionRegistry.removeSessionInformation(sessionId2);
assertNull(sessionRegistry.getSessionInformation(sessionId2));
assertNull(sessionRegistry.getAllSessions(principal, false));
}
// Clear final session
sessionRegistry.removeSessionInformation(sessionId2);
assertNull(sessionRegistry.getSessionInformation(sessionId2));
assertNull(sessionRegistry.getAllSessions(principal, false));
}
} }