Bug 511469 - Memory Leak in AbstractConnectionManager

Reuse existing session IDs instead of assigning new ones

Change-Id: I35441a7ef9aa01900feb32c9abe13fc00727ab31
diff --git a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/AccessControl.java b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/AccessControl.java
index 187e581..b434fe6 100644
--- a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/AccessControl.java
+++ b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/AccessControl.java
@@ -53,7 +53,7 @@
 	private final ESAuthorizationService authorizationService;
 	private final ESOrgUnitResolver orgUnitResolver;
 
-	private final ESSessions sessions;
+	private final EMFStoreSessions sessions;
 
 	private final LoginService loginService;
 
@@ -69,7 +69,7 @@
 	 */
 	public AccessControl(ServerSpace serverSpace) {
 		this.serverSpace = serverSpace;
-		sessions = new ESSessions();
+		sessions = new EMFStoreSessions();
 
 		orgUnitProvider = initOrgUnitProviderService();
 		orgUnitResolver = initOrgUnitResolverService();
@@ -91,7 +91,7 @@
 
 		this.authenticationControlType = authenticationControlType;
 		this.serverSpace = serverSpace;
-		sessions = new ESSessions();
+		sessions = new EMFStoreSessions();
 
 		orgUnitProvider = initOrgUnitProviderService();
 		orgUnitResolver = initOrgUnitResolverService();
diff --git a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/EMFStoreSessions.java b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/EMFStoreSessions.java
new file mode 100644
index 0000000..10de2b4
--- /dev/null
+++ b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/EMFStoreSessions.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2017 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * edgar - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emf.emfstore.internal.server.accesscontrol;
+
+import java.util.Map.Entry;
+
+import org.eclipse.emf.emfstore.internal.server.accesscontrol.authentication.ACUserContainer;
+import org.eclipse.emf.emfstore.internal.server.model.SessionId;
+import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.ACOrgUnitId;
+import org.eclipse.emf.emfstore.internal.server.model.impl.api.ESUserImpl;
+import org.eclipse.emf.emfstore.server.auth.ESSessions;
+import org.eclipse.emf.emfstore.server.model.ESSessionId;
+import org.eclipse.emf.emfstore.server.model.ESUser;
+
+/**
+ * Internal {@link ESSessions} implementation.
+ */
+public class EMFStoreSessions extends ESSessions {
+
+	/**
+	 * Given an user ID, tries to find a session.
+	 *
+	 * @param user the ID of the user whose session should be retrieved
+	 * @return the session ID, if any, {@code null} otherwise
+	 * @since 1.9
+	 */
+	public ESSessionId resolveByUser(ESUser user) {
+		final ACOrgUnitId userId = ((ESUserImpl) user).toInternalAPI().getId();
+		for (final Entry<SessionId, ACUserContainer> entry : sessionUserMap.entrySet()) {
+			final ACUserContainer container = entry.getValue();
+			if (container.getRawUser().getId().equals(userId)) {
+				return entry.getKey().toAPI();
+			}
+		}
+
+		return null;
+	}
+}
diff --git a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/LoginService.java b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/LoginService.java
index bafbd73..35ce4c9 100644
--- a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/LoginService.java
+++ b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/accesscontrol/LoginService.java
@@ -24,6 +24,7 @@
 import org.eclipse.emf.emfstore.internal.server.model.AuthenticationInformation;
 import org.eclipse.emf.emfstore.internal.server.model.accesscontrol.ACUser;
 import org.eclipse.emf.emfstore.internal.server.model.impl.api.ESAuthenticationInformationImpl;
+import org.eclipse.emf.emfstore.internal.server.model.impl.api.ESSessionIdImpl;
 import org.eclipse.emf.emfstore.internal.server.model.impl.api.ESUserImpl;
 import org.eclipse.emf.emfstore.server.auth.ESAuthenticationControlType;
 import org.eclipse.emf.emfstore.server.auth.ESOrgUnitResolver;
@@ -45,7 +46,7 @@
 	private static final String USER_VERIFIER_SERVICE_CLASS = "userVerifierServiceClass"; //$NON-NLS-1$
 	private static final String MONITOR_NAME = "authentication"; //$NON-NLS-1$
 	private static final String ACCESSCONTROL_EXTENSION_ID = "org.eclipse.emf.emfstore.server.accessControl"; //$NON-NLS-1$
-	private final ESSessions sessions;
+	private final EMFStoreSessions sessions;
 	private final ESOrgUnitResolver orgUnitResolver;
 	private final ESOrgUnitProvider orgUnitProvider;
 	private final ESAuthenticationControlType authenticationControlType;
@@ -67,7 +68,7 @@
 	 */
 	public LoginService(
 		ESAuthenticationControlType authenticationControlType,
-		ESSessions sessions,
+		EMFStoreSessions sessions,
 		ESOrgUnitProvider orgUnitProvider,
 		ESOrgUnitResolver orgUnitResolver) {
 
@@ -125,16 +126,22 @@
 				username,
 				password,
 				clientVersionInfo);
+			final AuthenticationInformation authenticationInformation = ESAuthenticationInformationImpl.class.cast(
+				authInfo).toInternalAPI();
 
-			sessions.add(authInfo);
+			final ESSessionId existingSession = sessions.resolveByUser(authInfo.getUser());
+
+			if (existingSession == null) {
+				sessions.add(authInfo);
+			} else {
+				authenticationInformation.setSessionId(
+					ESSessionIdImpl.class.cast(existingSession).toInternalAPI());
+			}
 
 			final ACUser resolvedUser = (ACUser) ESUserImpl.class.cast(
 				orgUnitResolver.resolveRoles(authInfo))
 				.toInternalAPI();
 
-			final AuthenticationInformation authenticationInformation = ESAuthenticationInformationImpl.class.cast(
-				authInfo).toInternalAPI();
-
 			authenticationInformation.setResolvedACUser(resolvedUser);
 			return authInfo;
 		}
diff --git a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/server/auth/ESSessions.java b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/server/auth/ESSessions.java
index 0f70a51..0a11acb 100644
--- a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/server/auth/ESSessions.java
+++ b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/server/auth/ESSessions.java
@@ -40,7 +40,12 @@
  */
 public class ESSessions {
 
-	private final Map<SessionId, ACUserContainer> sessionUserMap;
+	/**
+	 * Map holding valid session IDs and the respective users.
+	 *
+	 * @since 1.9
+	 */
+	protected final Map<SessionId, ACUserContainer> sessionUserMap;
 
 	/**
 	 * Default constructor.
diff --git a/tests/org.eclipse.emf.emfstore.client.test/src/org/eclipse/emf/emfstore/internal/client/test/workspace/AllWorkspaceTests.java b/tests/org.eclipse.emf.emfstore.client.test/src/org/eclipse/emf/emfstore/internal/client/test/workspace/AllWorkspaceTests.java
index 14901c3..7d73961 100644
--- a/tests/org.eclipse.emf.emfstore.client.test/src/org/eclipse/emf/emfstore/internal/client/test/workspace/AllWorkspaceTests.java
+++ b/tests/org.eclipse.emf.emfstore.client.test/src/org/eclipse/emf/emfstore/internal/client/test/workspace/AllWorkspaceTests.java
@@ -16,6 +16,7 @@
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
+	SessionTest.class,
 	WorkspaceTest.class,
 	ThreadLocalWorkSpaceTest.class
 })
diff --git a/tests/org.eclipse.emf.emfstore.client.test/src/org/eclipse/emf/emfstore/internal/client/test/workspace/SessionTest.java b/tests/org.eclipse.emf.emfstore.client.test/src/org/eclipse/emf/emfstore/internal/client/test/workspace/SessionTest.java
new file mode 100644
index 0000000..df9697b
--- /dev/null
+++ b/tests/org.eclipse.emf.emfstore.client.test/src/org/eclipse/emf/emfstore/internal/client/test/workspace/SessionTest.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2017 EclipseSource Muenchen GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Edgar Mueller - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emf.emfstore.internal.client.test.workspace;
+
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.emf.emfstore.client.test.common.cases.ESTestWithLoggedInUser;
+import org.eclipse.emf.emfstore.server.exceptions.ESException;
+import org.eclipse.emf.emfstore.server.model.ESSessionId;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class SessionTest extends ESTestWithLoggedInUser {
+
+	@BeforeClass
+	public static void beforeClass() {
+		startEMFStore();
+	}
+
+	@AfterClass
+	public static void afterClass() {
+		stopEMFStore();
+	}
+
+	@Test
+	public void stableSessionIdIfLoggedIn() throws ESException {
+		final ESSessionId sessionId = getUsersession().getSessionId();
+		getUsersession().refresh();
+		final ESSessionId sessionId2 = getUsersession().getSessionId();
+		assertEquals(sessionId, sessionId2);
+	}
+}