SEC-484: fixed concurrency issue
This commit is contained in:
parent
e70f01c260
commit
5ea8232f84
|
@ -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());
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue