details: https://code.openbravo.com/erp/devel/pi/rev/d126554d216b changeset: 33426:d126554d216b user: Asier Lostalé <asier.lostale <at> openbravo.com> date: Tue Feb 13 12:06:02 2018 +0100 summary: fixed bug 37893, fixed bug 37899: incorrect CU handling in concurrency and POS
Concurrent Users management had two different problems: * If a backoffice session was reused in POS closing backoffice browser, a CU session was counted and it was not deactivated while POS session was active. In this situation, the session should be deactivated if CU limit has been reached. * Code for creating and checking active http sessions in context was not thread safe, so it was possible to get an error when checking if session was active while other sessions were created/destroyed in paralell. This has been fixed by synchronizing on active session set. Having solved previous issue this should not create excessive contentention as it will be executed only if: CU limit has been reached and there are sessions created by mobile modules exclude POS. details: https://code.openbravo.com/erp/devel/pi/rev/4f7e83a9ad57 changeset: 33427:4f7e83a9ad57 user: Asier Lostalé <asier.lostale <at> openbravo.com> date: Tue Feb 13 12:08:43 2018 +0100 summary: related to bug 37893: added test case Which ensures sessions can be created and checked concurrently. diffstat: src-test/src/org/openbravo/test/AllAntTaskTests.java | 2 + src-test/src/org/openbravo/test/system/Sessions.java | 90 +++++++++++++++ src/org/openbravo/erpCommon/obps/ActivationKey.java | 12 ++ src/org/openbravo/erpCommon/security/SessionListener.java | 10 +- 4 files changed, 110 insertions(+), 4 deletions(-) diffs (176 lines): diff -r d6507984d651 -r 4f7e83a9ad57 src-test/src/org/openbravo/test/AllAntTaskTests.java --- a/src-test/src/org/openbravo/test/AllAntTaskTests.java Tue Feb 13 08:47:07 2018 +0100 +++ b/src-test/src/org/openbravo/test/AllAntTaskTests.java Tue Feb 13 12:08:43 2018 +0100 @@ -104,6 +104,7 @@ import org.openbravo.test.system.ErrorTextParserTest; import org.openbravo.test.system.ImportEntrySizeTest; import org.openbravo.test.system.Issue29934Test; +import org.openbravo.test.system.Sessions; import org.openbravo.test.system.SystemServiceTest; import org.openbravo.test.system.SystemValidatorTest; import org.openbravo.test.system.TestInfrastructure; @@ -194,6 +195,7 @@ Issue29934Test.class, // ImportEntrySizeTest.class, // CryptoUtilities.class, // + Sessions.class, // // xml ClientExportImportTest.class, // diff -r d6507984d651 -r 4f7e83a9ad57 src-test/src/org/openbravo/test/system/Sessions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src-test/src/org/openbravo/test/system/Sessions.java Tue Feb 13 12:08:43 2018 +0100 @@ -0,0 +1,90 @@ +/* + ************************************************************************* + * The contents of this file are subject to the Openbravo Public License + * Version 1.1 (the "License"), being the Mozilla Public License + * Version 1.1 with a permitted attribution clause; you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at http://www.openbravo.com/legal/license.html + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * The Original Code is Openbravo ERP. + * The Initial Developer of the Original Code is Openbravo SLU + * All portions are Copyright (C) 2018 Openbravo SLU + * All Rights Reserved. + * Contributor(s): ______________________________________. + ************************************************************************ + */ + +package org.openbravo.test.system; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionEvent; + +import org.jboss.arquillian.container.weld.ee.embedded_1_1.mock.MockHttpSession; +import org.junit.Test; +import org.openbravo.erpCommon.security.SessionListener; +import org.openbravo.erpCommon.utility.SequenceIdData; + +/** Test cases covering Session management */ +public class Sessions { + private static int NUMBER_OF_THREADS = 4; + private static int NUMBER_OF_SESSIONS_PER_THREAD = 1_000; + + /** Covers bug #37893 */ + @Test + public void canCreateAndCheckSessionsInParallel() throws InterruptedException, ExecutionException { + SessionListener sl = new SessionListener(); + List<SessionCreatorAndChecker> tasks = new ArrayList<>(); + for (int i = 0; i < NUMBER_OF_THREADS; i++) { + tasks.add(new SessionCreatorAndChecker(sl)); + } + + ExecutorService es = Executors.newFixedThreadPool(NUMBER_OF_THREADS); + List<Future<Void>> executions = es.invokeAll(tasks); + + for (Future<Void> e : executions) { + // if assertion failed, this will throw exception + e.get(); + } + } + + private class SessionCreatorAndChecker implements Callable<Void> { + private SessionListener sl; + + public SessionCreatorAndChecker(SessionListener sl) { + this.sl = sl; + } + + @Override + public Void call() { + for (int i = 0; i < NUMBER_OF_SESSIONS_PER_THREAD; i++) { + String sessionId = SequenceIdData.getUUID(); + + HttpSession s = new MockHttpSession(); + s.setAttribute("#AD_SESSION_ID", sessionId); + sl.sessionCreated(new HttpSessionEvent(s)); + + HttpSession sessionFromContext = SessionListener.getActiveSession(sessionId); + + // before fix of #37893, sessionFromContext is null in some cases because of caught + // ConcurrentModificationException + assertThat(sessionFromContext, is(s)); + } + + return null; + } + } +} diff -r d6507984d651 -r 4f7e83a9ad57 src/org/openbravo/erpCommon/obps/ActivationKey.java --- a/src/org/openbravo/erpCommon/obps/ActivationKey.java Tue Feb 13 08:47:07 2018 +0100 +++ b/src/org/openbravo/erpCommon/obps/ActivationKey.java Tue Feb 13 12:08:43 2018 +0100 @@ -40,6 +40,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; @@ -252,6 +253,12 @@ add("OBPOS_POS"); // WebPOS } }; + + private static final List<String> BACKOFFICE_SUCESS_SESSION_TYPES = Arrays.asList(// + "S", // Standard success session + "SUR" // Concurrent users soft limit reached + ); + public static final Long NO_LIMIT = -1L; private static ActivationKey instance = new ActivationKey(); @@ -1116,6 +1123,11 @@ * mobile apps) if activity from them has been recently detected. */ private boolean shouldDeactivateSession(Session expiredSession, Date lastValidPingTime) { + if (BACKOFFICE_SUCESS_SESSION_TYPES.contains(expiredSession.getLoginStatus())) { + // backoffice sessions use ping, they can be deactivated even if created in a different node + return true; + } + String sessionId = expiredSession.getId(); HttpSession session = SessionListener.getActiveSession(sessionId); if (session == null) { diff -r d6507984d651 -r 4f7e83a9ad57 src/org/openbravo/erpCommon/security/SessionListener.java --- a/src/org/openbravo/erpCommon/security/SessionListener.java Tue Feb 13 08:47:07 2018 +0100 +++ b/src/org/openbravo/erpCommon/security/SessionListener.java Tue Feb 13 12:08:43 2018 +0100 @@ -11,7 +11,7 @@ * under the License. * The Original Code is Openbravo ERP. * The Initial Developer of the Original Code is Openbravo SLU - * All portions are Copyright (C) 2009-2017 Openbravo SLU + * All portions are Copyright (C) 2009-2018 Openbravo SLU * All Rights Reserved. * Contributor(s): ______________________________________. ************************************************************************ @@ -175,9 +175,11 @@ */ public static HttpSession getActiveSession(String sessionId) { try { - for (HttpSession session : activeHttpSessions) { - if (sessionId.equals(session.getAttribute("#AD_SESSION_ID"))) { - return session; + synchronized (activeHttpSessions) { + for (HttpSession session : activeHttpSessions) { + if (sessionId.equals(session.getAttribute("#AD_SESSION_ID"))) { + return session; + } } } } catch (Exception e) { ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Openbravo-commits mailing list Openbravo-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openbravo-commits