From 66b2b4ff4c8e16ff0e39d044e262c783c69e643d Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 24 May 2024 16:59:56 -0500 Subject: [PATCH] CE-938 More flexible check in (can update message and control expires-timestamp) --- .../processes/locks/ProcessLockUtils.java | 132 ++++++++++++++++-- .../processes/locks/ProcessLockUtilsTest.java | 107 ++++++++++++++ 2 files changed, 225 insertions(+), 14 deletions(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/locks/ProcessLockUtils.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/locks/ProcessLockUtils.java index 7092ccd2..5b736c2a 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/locks/ProcessLockUtils.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/locks/ProcessLockUtils.java @@ -233,10 +233,68 @@ public class ProcessLockUtils /******************************************************************************* - ** + ** input wrapper for an overload of the checkin method, to allow more flexibility + ** w/ whether or not you want to update details & expiresAtTimestamp (e.g., so a + ** null can be passed in, to mean "set it to null" vs. "don't update it"). *******************************************************************************/ - public static void checkIn(ProcessLock processLock) + public static class CheckInInput { + private ProcessLock processLock; + private Instant expiresAtTimestamp = null; + private boolean wasGivenExpiresAtTimestamp = false; + private String details = null; + private boolean wasGivenDetails = false; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public CheckInInput(ProcessLock processLock) + { + this.processLock = processLock; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public CheckInInput withExpiresAtTimestamp(Instant expiresAtTimestamp) + { + this.expiresAtTimestamp = expiresAtTimestamp; + this.wasGivenExpiresAtTimestamp = true; + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public CheckInInput withDetails(String details) + { + this.details = details; + this.wasGivenDetails = true; + return (this); + } + } + + + + /******************************************************************************* + ** Do a check-in, with a specific value for the expiresAtTimestamp - which can + ** be set to null to make it null in the lock. + ** + ** If you don't want to specify the expiresAtTimestamp, call the overload that + ** doesn't take the timestamp - in which case it'll either stay the same as it + ** was, or will be set based on the type's default. + *******************************************************************************/ + public static void checkIn(CheckInInput input) + { + ProcessLock processLock = input.processLock; + try { if(processLock == null) @@ -245,25 +303,44 @@ public class ProcessLockUtils return; } - ProcessLockType lockType = getProcessLockTypeById(processLock.getProcessLockTypeId()); - if(lockType == null) - { - throw (new QException("Unrecognized process lock type id: " + processLock.getProcessLockTypeId())); - } - - Instant now = Instant.now(); QRecord recordToUpdate = new QRecord() .withValue("id", processLock.getId()) - .withValue("checkInTimestamp", now); + .withValue("checkInTimestamp", Instant.now()); - Integer defaultExpirationSeconds = lockType.getDefaultExpirationSeconds(); - if(defaultExpirationSeconds != null) + /////////////////////////////////////////////////////////////////// + // if the input was given a details string, update the details // + // use boolean instead of null to know whether or not to do this // + /////////////////////////////////////////////////////////////////// + if(input.wasGivenDetails) { - recordToUpdate.setValue("expiresAtTimestamp", now.plusSeconds(defaultExpirationSeconds)); + recordToUpdate.setValue("details", input.details); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // if the input object had an expires-at timestamp put in it, then use that value (null or otherwise) for the expires-at-timestamp // + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if(input.wasGivenExpiresAtTimestamp) + { + recordToUpdate.setValue("expiresAtTimestamp", input.expiresAtTimestamp); + } + else + { + //////////////////////////////////////////////////////////////////////////////// + // else, do the default thing - which is, look for a default in the lock type // + //////////////////////////////////////////////////////////////////////////////// + ProcessLockType lockType = getProcessLockTypeById(processLock.getProcessLockTypeId()); + if(lockType != null) + { + Integer defaultExpirationSeconds = lockType.getDefaultExpirationSeconds(); + if(defaultExpirationSeconds != null) + { + recordToUpdate.setValue("expiresAtTimestamp", Instant.now().plusSeconds(defaultExpirationSeconds)); + } + } } new UpdateAction().execute(new UpdateInput(ProcessLock.TABLE_NAME).withRecord(recordToUpdate)); - LOG.debug("Updated processLock checkInTimestamp", logPair("id", processLock.getId()), logPair("checkInTimestamp", now)); + LOG.debug("Checked in on process lock", logPair("id", processLock.getId())); } catch(Exception e) { @@ -273,6 +350,33 @@ public class ProcessLockUtils + /******************************************************************************* + ** Do a check-in, with a specific value for the expiresAtTimestamp - which can + ** be set to null to make it null in the lock. + ** + ** If you don't want to specify the expiresAtTimestamp, call the overload that + ** doesn't take the timestamp - in which case it'll either stay the same as it + ** was, or will be set based on the type's default. + *******************************************************************************/ + public static void checkIn(ProcessLock processLock, Instant expiresAtTimestamp) + { + checkIn(new CheckInInput(processLock).withExpiresAtTimestamp(expiresAtTimestamp)); + } + + + + /******************************************************************************* + ** Do a check-in, updating the expires-timestamp based on the lock type's default. + ** (or leaving it the same as it was (null or otherwise) if there is no default + ** on the type). + *******************************************************************************/ + public static void checkIn(ProcessLock processLock) + { + checkIn(new CheckInInput(processLock)); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/locks/ProcessLockUtilsTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/locks/ProcessLockUtilsTest.java index 3a08f672..2d58a3fd 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/locks/ProcessLockUtilsTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/processes/locks/ProcessLockUtilsTest.java @@ -271,4 +271,111 @@ class ProcessLockUtilsTest extends BaseTest } } + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testCheckInExpiresAtTimestampsWithNoDefault() throws QException + { + ///////////////////////////////////////// + // this type has no default expiration // + ///////////////////////////////////////// + ProcessLock processLock = ProcessLockUtils.create("1", "typeA", null); + assertNull(processLock.getExpiresAtTimestamp()); + + ///////////////////////////////////////////////////////////// + // checkin w/o specifying an expires-time - leaves it null // + ///////////////////////////////////////////////////////////// + ProcessLockUtils.checkIn(processLock); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertNull(processLock.getExpiresAtTimestamp()); + + ////////////////////////////////////////////// + // checkin specifying null - leaves it null // + ////////////////////////////////////////////// + ProcessLockUtils.checkIn(processLock, null); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertNull(processLock.getExpiresAtTimestamp()); + + ////////////////////////////////////////////// + // checkin w/ a time - sets it to that time // + ////////////////////////////////////////////// + Instant specifiedTime = Instant.now(); + ProcessLockUtils.checkIn(processLock, specifiedTime); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertEquals(specifiedTime, processLock.getExpiresAtTimestamp()); + + /////////////////////////////////////////////////////////// + // checkin w/o specifying time - leaves it previous time // + /////////////////////////////////////////////////////////// + SleepUtils.sleep(1, TimeUnit.MILLISECONDS); + ProcessLockUtils.checkIn(processLock); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertEquals(specifiedTime, processLock.getExpiresAtTimestamp()); + + //////////////////////////////////////////////////// + // checkin specifying null - puts it back to null // + //////////////////////////////////////////////////// + ProcessLockUtils.checkIn(processLock, null); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertNull(processLock.getExpiresAtTimestamp()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testCheckInExpiresAtTimestampsWithSomeDefault() throws QException + { + ///////////////////////////////////////// + // this type has a default expiration // + ///////////////////////////////////////// + ProcessLock processLock = ProcessLockUtils.create("1", "typeB", null); + assertNotNull(processLock.getExpiresAtTimestamp()); + Instant expiresAtTimestamp = processLock.getExpiresAtTimestamp(); + + /////////////////////////////////////////////////////////////// + // checkin w/o specifying an expires-time - moves it forward // + /////////////////////////////////////////////////////////////// + SleepUtils.sleep(1, TimeUnit.MILLISECONDS); + ProcessLockUtils.checkIn(processLock); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertNotNull(processLock.getExpiresAtTimestamp()); + assertNotEquals(expiresAtTimestamp, processLock.getExpiresAtTimestamp()); + + /////////////////////////////////////////////// + // checkin specifying null - sets it to null // + /////////////////////////////////////////////// + ProcessLockUtils.checkIn(processLock, null); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertNull(processLock.getExpiresAtTimestamp()); + + ////////////////////////////////////////////// + // checkin w/ a time - sets it to that time // + ////////////////////////////////////////////// + Instant specifiedTime = Instant.now(); + ProcessLockUtils.checkIn(processLock, specifiedTime); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertEquals(specifiedTime, processLock.getExpiresAtTimestamp()); + + ///////////////////////////////////////////////////////////////////////// + // checkin w/o specifying time - uses the default and moves it forward // + ///////////////////////////////////////////////////////////////////////// + SleepUtils.sleep(1, TimeUnit.MILLISECONDS); + ProcessLockUtils.checkIn(processLock); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertNotEquals(specifiedTime, processLock.getExpiresAtTimestamp()); + + //////////////////////////////////////////////////// + // checkin specifying null - puts it back to null // + //////////////////////////////////////////////////// + ProcessLockUtils.checkIn(processLock, null); + processLock = ProcessLockUtils.getById(processLock.getId()); + assertNull(processLock.getExpiresAtTimestamp()); + } + } \ No newline at end of file