From 40f98fa5944675439061f93ce1858c33e1b70952 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:36:52 +0000 Subject: [PATCH 1/2] Initial plan From 67a236d397076f379cfc1a02549f22ceab1ad558 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:50:57 +0000 Subject: [PATCH 2/2] docs: Add missing Javadoc comments to resolve build warnings Co-authored-by: JaredHatfield <208119+JaredHatfield@users.noreply.github.com> --- .../lockservicecentral/api/App.java | 7 +- .../api/config/AudienceClaimValidator.java | 2 +- .../EpochTimeProviderConfiguration.java | 7 +- .../config/JsonValidationConfiguration.java | 2 +- .../config/MyCustomSecurityConfiguration.java | 15 +++- .../config/UuidGeneratorConfiguration.java | 7 +- .../api/config/WebMvcConfig.java | 2 +- .../api/controller/LockController.java | 16 ++-- .../api/dto/InternalErrorResponse.java | 6 +- .../api/dto/ValidationErrorResponse.java | 4 +- .../api/handler/GlobalExceptionHandler.java | 34 +++++++- .../api/service/LockManagerService.java | 22 ++--- .../api/APILockServiceTest.java | 2 +- .../StaticEpochTimeProviderConfiguration.java | 2 +- .../StaticUuidGeneratorConfiguration.java | 2 +- .../api/dto/InternalErrorResponseTest.java | 2 +- .../locker/dynamodb/DynamoDbLockService.java | 34 ++++---- .../LockerAwsDynamoDbClientConfig.java | 7 +- .../dynamodb/LockerAwsDynamoDbConfig.java | 8 +- .../DynamoDbLockServiceActualTest.java | 8 +- .../dynamodb/DynamoDbLockServiceTest.java | 2 +- .../locker/etcd/EtcdLockService.java | 12 +-- .../locker/etcd/LockerEtcdClientConfig.java | 7 +- .../locker/etcd/LockerEtcdConfig.java | 8 +- .../etcd/EtcdLockServiceActualTest.java | 2 +- .../locker/etcd/EtcdLockServiceTest.java | 2 +- .../firestore/FirestoreLockService.java | 6 +- .../locker/firestore/LockerGcpConfig.java | 8 +- .../firestore/LockerGcpFirestoreConfig.java | 7 +- .../FirestoreLockServiceActualTest.java | 2 +- .../firestore/FirestoreLockServiceTest.java | 2 +- .../locker/memory/LockerMemoryConfig.java | 8 +- .../locker/memory/MemoryLockService.java | 16 ++-- .../locker/memory/MemoryLockServiceTest.java | 2 +- .../locker/tests/AbstractLockServiceTest.java | 87 ++++++++++++++++++- .../lockservicecentral/locker/Lock.java | 49 ++++++++++- .../lockservicecentral/locker/LockAction.java | 2 +- .../locker/LockService.java | 10 +-- .../lockservicecentral/locker/LockTest.java | 2 +- .../logging/AppRuntimeProperties.java | 2 +- .../logging/CanonicalEmitInterceptor.java | 12 +-- .../logging/CanonicalInitFilter.java | 8 +- .../logging/CanonicalLogContext.java | 30 +++---- .../logging/CanonicalLogger.java | 10 +-- .../logging/CanonicalLoggingConfig.java | 6 +- .../lockservicecentral/logging/HashUtil.java | 6 +- .../logging/CanonicalEmitInterceptorTest.java | 2 +- .../logging/CanonicalLogContextTest.java | 2 +- .../logging/HashUtilTest.java | 2 +- 49 files changed, 362 insertions(+), 141 deletions(-) diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/App.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/App.java index 0cc552b..33b04c7 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/App.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/App.java @@ -19,12 +19,17 @@ /** * The Spring Boot 3 app for the token server - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @SpringBootApplication @ComponentScan(basePackages = { "com.unitvectory.lockservicecentral" }) public class App { + /** + * The main entry point for the application. + * + * @param args command-line arguments + */ public static void main(String[] args) { SpringApplication.run(App.class, args); } diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/AudienceClaimValidator.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/AudienceClaimValidator.java index f9a38c5..35c859d 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/AudienceClaimValidator.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/AudienceClaimValidator.java @@ -24,7 +24,7 @@ /** * Custom logic to validate the aud claim in the JWT - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @AllArgsConstructor diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/EpochTimeProviderConfiguration.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/EpochTimeProviderConfiguration.java index 9f0bfa6..ac92448 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/EpochTimeProviderConfiguration.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/EpochTimeProviderConfiguration.java @@ -22,13 +22,18 @@ /** * The Time Configuration - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @Profile("!time-disabled") public class EpochTimeProviderConfiguration { + /** + * Creates the EpochTimeProvider bean. + * + * @return the EpochTimeProvider instance + */ @Bean public EpochTimeProvider epochTimeProvider() { return SystemEpochTimeProvider.getInstance(); diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/JsonValidationConfiguration.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/JsonValidationConfiguration.java index c10caad..af6558d 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/JsonValidationConfiguration.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/JsonValidationConfiguration.java @@ -21,7 +21,7 @@ /** * The JSON validation configuration - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/MyCustomSecurityConfiguration.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/MyCustomSecurityConfiguration.java index 35a7953..4f3b98c 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/MyCustomSecurityConfiguration.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/MyCustomSecurityConfiguration.java @@ -31,17 +31,17 @@ /** * Security Configuration - * + * * Configures JWT validation based on `jwt.issuer` and `jwt.jwks` settings. * If neither is set, no authentication is required. - * + * * If either is set, JWT authentication is required. * JWKS is prioritized, but OpenID Connect discovery is used if only the issuer * is set. - * + * * If `jwt.issue` is set, the issuer claim is validated. * If `jwt.audience` is set, the audience claim is validated. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @@ -61,6 +61,13 @@ public class MyCustomSecurityConfiguration { @Value("${jwt.audience:#{null}}") private String audience; + /** + * Creates the SecurityFilterChain bean. + * + * @param http the HttpSecurity to configure + * @return the configured SecurityFilterChain + * @throws Exception if an error occurs during configuration + */ @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { if (this.issuer != null || this.jwks != null) { diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/UuidGeneratorConfiguration.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/UuidGeneratorConfiguration.java index e8c83d6..3c4158a 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/UuidGeneratorConfiguration.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/UuidGeneratorConfiguration.java @@ -22,13 +22,18 @@ /** * The UUID Generator Configuration - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @Profile("!uuid-disabled") public class UuidGeneratorConfiguration { + /** + * Creates the UuidGenerator bean. + * + * @return the UuidGenerator instance + */ @Bean public UuidGenerator uuidGenerator() { return RandomUuidGenerator.getInstance(); diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/WebMvcConfig.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/WebMvcConfig.java index cd69a47..e4035cc 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/config/WebMvcConfig.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/config/WebMvcConfig.java @@ -22,7 +22,7 @@ /** * Web MVC configuration to register the canonical emit interceptor. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/controller/LockController.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/controller/LockController.java index 6706ad7..70f573e 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/controller/LockController.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/controller/LockController.java @@ -37,7 +37,7 @@ /** * The Lock Controller - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @RestController @@ -51,11 +51,11 @@ public class LockController { /** * Gets the status of a lock. - * + * * The status of the lock is always returned, even if the lock does not exist as * that is just an available lock. The locks that are unavailable are the ones * that include the `owner` and `expiry` fields. - * + * * @param namespace the lock namespace * @param lockName the lock name * @return the lock status @@ -74,7 +74,7 @@ public ResponseEntity getLock( /** * Acquire a lock. - * + * * @param namespace the lock namespace * @param lockName the lock name * @param lock the lock request @@ -99,7 +99,7 @@ public ResponseEntity acquireLock( /** * Renew a lock. - * + * * @param namespace the lock namespace * @param lockName the lock name * @param lock the lock request @@ -124,7 +124,7 @@ public ResponseEntity renewLock( /** * Release a lock. - * + * * @param namespace the lock namespace * @param lockName the lock name * @param lock the lock request @@ -149,7 +149,7 @@ public ResponseEntity releaseLock( /** * Sets the lock attributes. - * + * * @param lock the lock * @param namespace the namespace * @param lockName the lock name @@ -167,7 +167,7 @@ private void setLockAttributes(@NonNull Lock lock, @NonNull String namespace, @N /** * Enriches the canonical log context with lock operation details. - * + * * @param namespace the lock namespace * @param lockName the lock name * @param operation the lock operation diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/dto/InternalErrorResponse.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/dto/InternalErrorResponse.java index 3db2bbb..a4d0891 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/dto/InternalErrorResponse.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/dto/InternalErrorResponse.java @@ -17,7 +17,7 @@ /** * The internal error response - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Getter @@ -35,7 +35,7 @@ public class InternalErrorResponse { /** * Create a new internal error response - * + * * @param errorId the error ID */ public InternalErrorResponse(String errorId) { @@ -45,7 +45,7 @@ public InternalErrorResponse(String errorId) { /** * Create a new internal error response - * + * * @param errorId the error ID * @param message the message */ diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/dto/ValidationErrorResponse.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/dto/ValidationErrorResponse.java index ae331aa..e847ed1 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/dto/ValidationErrorResponse.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/dto/ValidationErrorResponse.java @@ -25,7 +25,7 @@ /** * The validation error response - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Getter @@ -44,7 +44,7 @@ public class ValidationErrorResponse { /** * Create a new validation error response from a handler method validation - * + * * @param ex the handler method validation exception */ public ValidationErrorResponse(HandlerMethodValidationException ex) { diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/handler/GlobalExceptionHandler.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/handler/GlobalExceptionHandler.java index e812329..b5d83ac 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/handler/GlobalExceptionHandler.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/handler/GlobalExceptionHandler.java @@ -34,7 +34,7 @@ /** * The global exception handler - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @ControllerAdvice @@ -47,6 +47,12 @@ public class GlobalExceptionHandler { @Autowired private ObjectProvider canonicalLogContextProvider; + /** + * Handles ValidateJsonSchemaException. + * + * @param ex the exception + * @return the error response + */ @ExceptionHandler(ValidateJsonSchemaException.class) public ResponseEntity onValidateJsonSchemaException( ValidateJsonSchemaException ex) { @@ -54,6 +60,12 @@ public ResponseEntity onValidateJsonSchemaExce return ResponseEntity.badRequest().body(new ValidateJsonSchemaFailedResponse(ex)); } + /** + * Handles HandlerMethodValidationException. + * + * @param ex the exception + * @return the error response + */ @ExceptionHandler(HandlerMethodValidationException.class) public ResponseEntity onHandlerMethodValidationException( HandlerMethodValidationException ex) { @@ -61,6 +73,12 @@ public ResponseEntity onHandlerMethodValidationExceptio return ResponseEntity.badRequest().body(new ValidationErrorResponse(ex)); } + /** + * Handles HttpRequestMethodNotSupportedException. + * + * @param ex the exception + * @return the error response + */ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResponseEntity onHttpRequestMethodNotSupportedException( HttpRequestMethodNotSupportedException ex) { @@ -69,6 +87,12 @@ public ResponseEntity onHttpRequestMethodNotSupportedExce .body(new InternalErrorResponse(this.uuidGenerator.generateUuid(), "Method not allowed")); } + /** + * Handles NoResourceFoundException. + * + * @param ex the exception + * @return the error response + */ @ExceptionHandler(NoResourceFoundException.class) public ResponseEntity onNoResourceFoundException(NoResourceFoundException ex) { // Don't log stack trace for client errors like 404 @@ -76,6 +100,12 @@ public ResponseEntity onNoResourceFoundException(NoResour .body(new InternalErrorResponse(this.uuidGenerator.generateUuid(), "Resource not found")); } + /** + * Handles all other exceptions. + * + * @param ex the exception + * @return the error response + */ @ExceptionHandler(Exception.class) public ResponseEntity onException(Exception ex) { // This will generate a unique error ID for each error @@ -105,7 +135,7 @@ private void enrichCanonicalContextForValidationError() { /** * Enriches the canonical log context for unhandled exceptions. - * + * * @param ex the exception * @param errorId the error ID for correlation */ diff --git a/api/src/main/java/com/unitvectory/lockservicecentral/api/service/LockManagerService.java b/api/src/main/java/com/unitvectory/lockservicecentral/api/service/LockManagerService.java index 3e09b79..4665158 100644 --- a/api/src/main/java/com/unitvectory/lockservicecentral/api/service/LockManagerService.java +++ b/api/src/main/java/com/unitvectory/lockservicecentral/api/service/LockManagerService.java @@ -28,7 +28,7 @@ /** * The Lock Service - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Service @@ -48,7 +48,7 @@ public class LockManagerService { /** * Get a lock. - * + * * @param namespace the namespace * @param lockName the lock name * @return the lock @@ -80,7 +80,7 @@ public Lock getLock(@NonNull String namespace, @NonNull String lockName) { /** * Acquire a lock. - * + * * @param lock the lock request * @return the lock response */ @@ -98,7 +98,7 @@ public Lock acquireLock(@NonNull Lock lock) { // Determine lock result String lockResult = Boolean.TRUE.equals(result.getSuccess()) ? "success" : "conflict"; - + // Enrich canonical context enrichCanonicalContext(endTime - startTime, lockResult, result.getSuccess() ? expiry : null); @@ -107,7 +107,7 @@ public Lock acquireLock(@NonNull Lock lock) { /** * Renew a lock. - * + * * @param lock the lock request * @return the lock response */ @@ -123,7 +123,7 @@ public Lock renewLock(@NonNull Lock lock) { // Determine lock result String lockResult = Boolean.TRUE.equals(result.getSuccess()) ? "success" : "conflict"; - + // Enrich canonical context with new expiry Long computedExpiry = Boolean.TRUE.equals(result.getSuccess()) ? result.getExpiry() : null; enrichCanonicalContext(endTime - startTime, lockResult, computedExpiry); @@ -133,7 +133,7 @@ public Lock renewLock(@NonNull Lock lock) { /** * Release a lock. - * + * * @param lock the lock request * @return the lock response */ @@ -149,7 +149,7 @@ public Lock releaseLock(@NonNull Lock lock) { // Determine lock result String lockResult = Boolean.TRUE.equals(result.getSuccess()) ? "success" : "conflict"; - + // Enrich canonical context enrichCanonicalContext(endTime - startTime, lockResult, null); @@ -158,18 +158,18 @@ public Lock releaseLock(@NonNull Lock lock) { /** * Enriches the canonical log context with service-level details. - * + * * @param backendDurationMs time spent in backend call * @param lockResult the lock operation result * @param computedExpiry the computed expiry (for acquire/renew) */ private void enrichCanonicalContext(long backendDurationMs, String lockResult, Long computedExpiry) { CanonicalLogContext context = canonicalLogContextProvider.getObject(); - + context.put("lock_backend", lockBackend); context.put("backend_duration_ms", backendDurationMs); context.put("lock_result", lockResult); - + if (computedExpiry != null) { context.put("computed_expiry_epoch_sec", computedExpiry); } diff --git a/api/src/test/java/com/unitvectory/lockservicecentral/api/APILockServiceTest.java b/api/src/test/java/com/unitvectory/lockservicecentral/api/APILockServiceTest.java index 595567d..9a6f54b 100644 --- a/api/src/test/java/com/unitvectory/lockservicecentral/api/APILockServiceTest.java +++ b/api/src/test/java/com/unitvectory/lockservicecentral/api/APILockServiceTest.java @@ -39,7 +39,7 @@ /** * The parameterized tests for the Spring Boot API - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) diff --git a/api/src/test/java/com/unitvectory/lockservicecentral/api/config/StaticEpochTimeProviderConfiguration.java b/api/src/test/java/com/unitvectory/lockservicecentral/api/config/StaticEpochTimeProviderConfiguration.java index 1180f81..aafa7c7 100644 --- a/api/src/test/java/com/unitvectory/lockservicecentral/api/config/StaticEpochTimeProviderConfiguration.java +++ b/api/src/test/java/com/unitvectory/lockservicecentral/api/config/StaticEpochTimeProviderConfiguration.java @@ -22,7 +22,7 @@ /** * The Static Time Configuration - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration diff --git a/api/src/test/java/com/unitvectory/lockservicecentral/api/config/StaticUuidGeneratorConfiguration.java b/api/src/test/java/com/unitvectory/lockservicecentral/api/config/StaticUuidGeneratorConfiguration.java index 45e77a9..d9f2015 100644 --- a/api/src/test/java/com/unitvectory/lockservicecentral/api/config/StaticUuidGeneratorConfiguration.java +++ b/api/src/test/java/com/unitvectory/lockservicecentral/api/config/StaticUuidGeneratorConfiguration.java @@ -22,7 +22,7 @@ /** * The Static UUID Generator Configuration - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration diff --git a/api/src/test/java/com/unitvectory/lockservicecentral/api/dto/InternalErrorResponseTest.java b/api/src/test/java/com/unitvectory/lockservicecentral/api/dto/InternalErrorResponseTest.java index 6b35fc4..304a01a 100644 --- a/api/src/test/java/com/unitvectory/lockservicecentral/api/dto/InternalErrorResponseTest.java +++ b/api/src/test/java/com/unitvectory/lockservicecentral/api/dto/InternalErrorResponseTest.java @@ -19,7 +19,7 @@ /** * The InternalErrorResponse tests - * + * * @author Jared Hatfield (UnitVectorY Labs) */ public class InternalErrorResponseTest { diff --git a/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockService.java b/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockService.java index 354d933..924e376 100644 --- a/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockService.java +++ b/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockService.java @@ -39,12 +39,12 @@ /** * DynamoDB implementation of {@link LockService} providing distributed lock functionality. - * + * *

This implementation uses DynamoDB's conditional write operations to ensure atomic * lock operations across distributed instances. All lock mutations (acquire, renew, release) * are performed using single atomic operations with comprehensive condition expressions, * eliminating race conditions that would occur with read-then-write patterns.

- * + * *

Atomicity Guarantees

*
    *
  • Acquire: Single PutItem with condition that succeeds only if the lock doesn't @@ -54,12 +54,12 @@ *
  • Release: Single DeleteItem with condition that succeeds only if the lock * matches the owner/instance (expired locks are also deletable)
  • *
- * + * *

Lock Expiry Handling

*

Lock expiry is checked atomically within DynamoDB condition expressions using the * current timestamp passed to each operation. This ensures that expiry checks cannot * be affected by clock skew between read and write operations.

- * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Slf4j @@ -71,7 +71,7 @@ public class DynamoDbLockService implements LockService { /** * Constructs a new DynamoDbLockService. - * + * * @param dynamoDbClient the DynamoDB client * @param tableName the DynamoDB table name for locks * @param canonicalLogContextProvider provider for the canonical log context @@ -85,7 +85,7 @@ public DynamoDbLockService(DynamoDbClient dynamoDbClient, String tableName, /** * Records the lock service outcome to the canonical log context. - * + * * @param outcome the screaming snake case outcome */ private void recordOutcome(String outcome) { @@ -99,7 +99,7 @@ private void recordOutcome(String outcome) { /** * Generates the DynamoDB primary key for a lock based on namespace and lock name. - * + * * @param namespace the namespace * @param lockName the lock name * @return the primary key value @@ -110,7 +110,7 @@ private String generateKey(String namespace, String lockName) { /** * Converts a DynamoDB item to a Lock object. - * + * * @param item the DynamoDB item * @return the Lock object, or null if item is empty */ @@ -132,14 +132,14 @@ private Lock itemToLock(Map item) { /** * Converts a Lock object to a DynamoDB item. - * + * * @param lock the Lock object * @return the DynamoDB item */ private Map lockToItem(Lock lock) { Map item = new HashMap<>(); String key = generateKey(lock.getNamespace(), lock.getLockName()); - + item.put("lockId", AttributeValue.builder().s(key).build()); item.put("namespace", AttributeValue.builder().s(lock.getNamespace()).build()); item.put("lockName", AttributeValue.builder().s(lock.getLockName()).build()); @@ -184,7 +184,7 @@ public Lock acquireLock(@NonNull Lock originalLock, long now) { // Atomic condition: lock doesn't exist OR lock is expired OR same owner/instance // This handles all acquire scenarios in a single atomic operation - String conditionExpression = + String conditionExpression = "attribute_not_exists(lockId) OR " + "expiry < :now OR " + "(#owner = :owner AND instanceId = :instanceId)"; @@ -231,14 +231,14 @@ public Lock renewLock(@NonNull Lock originalLock, long now) { try { // Atomic condition: lock exists, is not expired, and matches owner/instance // Note: attribute_exists is required because UpdateItem would otherwise create the item - String conditionExpression = + String conditionExpression = "attribute_exists(lockId) AND " + "expiry >= :now AND " + "#owner = :owner AND " + "instanceId = :instanceId"; // Update expression: add leaseDuration to both leaseDuration and expiry - String updateExpression = + String updateExpression = "SET leaseDuration = leaseDuration + :addDuration, " + "expiry = expiry + :addDuration"; @@ -262,7 +262,7 @@ public Lock renewLock(@NonNull Lock originalLock, long now) { .build(); UpdateItemResponse response = dynamoDbClient.updateItem(updateRequest); - + // Extract the updated values from the response Map attrs = response.attributes(); if (attrs != null && !attrs.isEmpty()) { @@ -272,7 +272,7 @@ public Lock renewLock(@NonNull Lock originalLock, long now) { lock.setExpiry(updatedLock.getExpiry()); } } - + lock.setSuccess(); recordOutcome("RENEWED"); @@ -298,7 +298,7 @@ public Lock releaseLock(@NonNull Lock originalLock, long now) { try { // Atomic condition: lock must match owner and instanceId // We allow releasing expired locks that we own - String conditionExpression = + String conditionExpression = "#owner = :owner AND instanceId = :instanceId"; Map expressionValues = new HashMap<>(); @@ -325,7 +325,7 @@ public Lock releaseLock(@NonNull Lock originalLock, long now) { // Condition failed: could mean lock doesn't exist or belongs to different owner // Check the item from the exception to determine which case Map item = e.item(); - + if (item == null || item.isEmpty()) { // Lock doesn't exist - treat as success (already released) lock.setCleared(); diff --git a/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/LockerAwsDynamoDbClientConfig.java b/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/LockerAwsDynamoDbClientConfig.java index 1967f77..194004d 100644 --- a/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/LockerAwsDynamoDbClientConfig.java +++ b/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/LockerAwsDynamoDbClientConfig.java @@ -29,7 +29,7 @@ /** * The Configuration for the DynamoDB Client. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @@ -47,6 +47,11 @@ public class LockerAwsDynamoDbClientConfig { @Value("${locker.dynamodb.secretAccessKey:#{null}}") private String secretAccessKey; + /** + * Creates the DynamoDbClient bean. + * + * @return the DynamoDbClient instance + */ @Bean public DynamoDbClient dynamoDbClient() { DynamoDbClientBuilder builder = DynamoDbClient.builder() diff --git a/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/LockerAwsDynamoDbConfig.java b/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/LockerAwsDynamoDbConfig.java index 2f91675..99c473f 100644 --- a/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/LockerAwsDynamoDbConfig.java +++ b/locker-dynamodb/src/main/java/com/unitvectory/lockservicecentral/locker/dynamodb/LockerAwsDynamoDbConfig.java @@ -26,7 +26,7 @@ /** * The Configuration for the DynamoDB LockService. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @@ -38,6 +38,12 @@ public class LockerAwsDynamoDbConfig { @Value("${locker.dynamodb.tableName:locks}") private String tableName; + /** + * Creates the LockService bean. + * + * @param canonicalLogContextProvider the canonical log context provider + * @return the LockService instance + */ @Bean public LockService lockService(ObjectProvider canonicalLogContextProvider) { return new DynamoDbLockService(this.dynamoDbClient, this.tableName, canonicalLogContextProvider); diff --git a/locker-dynamodb/src/test/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockServiceActualTest.java b/locker-dynamodb/src/test/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockServiceActualTest.java index 3ca6d4c..edfd341 100644 --- a/locker-dynamodb/src/test/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockServiceActualTest.java +++ b/locker-dynamodb/src/test/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockServiceActualTest.java @@ -29,7 +29,7 @@ /** * The DynamoDbLockService test with actual DynamoDB server. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Disabled @@ -40,7 +40,7 @@ protected LockService createLockService() { // These tests are disabled because they require interaction with an actual // DynamoDB server (or DynamoDB Local) to run. These are only intended to be used for manual // local testing. - + // For DynamoDB Local running on localhost:8000 DynamoDbClient client = DynamoDbClient.builder() .region(Region.US_EAST_1) @@ -48,7 +48,7 @@ protected LockService createLockService() { .credentialsProvider(StaticCredentialsProvider.create( AwsBasicCredentials.create("dummy", "dummy"))) .build(); - + // Use a no-op ObjectProvider for testing ObjectProvider noOpProvider = new ObjectProvider<>() { @Override @@ -71,7 +71,7 @@ public CanonicalLogContext getIfUnique() { return new CanonicalLogContext(); } }; - + return new DynamoDbLockService(client, "locks", noOpProvider); } diff --git a/locker-dynamodb/src/test/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockServiceTest.java b/locker-dynamodb/src/test/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockServiceTest.java index d23e3c8..24f9328 100644 --- a/locker-dynamodb/src/test/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockServiceTest.java +++ b/locker-dynamodb/src/test/java/com/unitvectory/lockservicecentral/locker/dynamodb/DynamoDbLockServiceTest.java @@ -34,7 +34,7 @@ /** * The DynamoDbLockService test. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ public class DynamoDbLockServiceTest { diff --git a/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockService.java b/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockService.java index a4db09e..6101d2d 100644 --- a/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockService.java +++ b/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockService.java @@ -48,7 +48,7 @@ /** * The etcd LockService. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Slf4j @@ -66,7 +66,7 @@ public class EtcdLockService implements LockService { /** * Constructs a new EtcdLockService. - * + * * @param client the etcd client * @param keyPrefix the key prefix for locks * @param maxRetries the maximum number of retries @@ -84,7 +84,7 @@ public EtcdLockService(Client client, String keyPrefix, int maxRetries, long req /** * Records the lock service outcome to the canonical log context. - * + * * @param outcome the screaming snake case outcome */ private void recordOutcome(String outcome) { @@ -98,7 +98,7 @@ private void recordOutcome(String outcome) { /** * Generates the etcd key for a lock based on namespace and lock name. - * + * * @param namespace the namespace * @param lockName the lock name * @return the etcd key as ByteSequence @@ -110,7 +110,7 @@ private ByteSequence generateKey(String namespace, String lockName) { /** * Serializes a Lock to JSON bytes for storage in etcd. - * + * * @param lock the lock to serialize * @return the JSON bytes * @throws JsonProcessingException if serialization fails @@ -123,7 +123,7 @@ private ByteSequence serializeLock(Lock lock) throws JsonProcessingException { /** * Deserializes a Lock from JSON bytes stored in etcd. - * + * * @param value the JSON bytes * @return the Lock object * @throws JsonProcessingException if deserialization fails diff --git a/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/LockerEtcdClientConfig.java b/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/LockerEtcdClientConfig.java index d8af811..4f137a3 100644 --- a/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/LockerEtcdClientConfig.java +++ b/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/LockerEtcdClientConfig.java @@ -28,7 +28,7 @@ /** * The Configuration for the etcd Client. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @@ -56,6 +56,11 @@ public class LockerEtcdClientConfig { @Value("${locker.etcd.auth.password:#{null}}") private String password; + /** + * Creates the etcd Client bean. + * + * @return the etcd Client instance + */ @Bean public Client etcdClient() { String[] endpointArray = endpoints.split(","); diff --git a/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/LockerEtcdConfig.java b/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/LockerEtcdConfig.java index cb94009..09be2d7 100644 --- a/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/LockerEtcdConfig.java +++ b/locker-etcd/src/main/java/com/unitvectory/lockservicecentral/locker/etcd/LockerEtcdConfig.java @@ -26,7 +26,7 @@ /** * The Configuration for the etcd LockService. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @@ -44,6 +44,12 @@ public class LockerEtcdConfig { @Value("${locker.etcd.requestTimeoutMs:5000}") private long requestTimeoutMs; + /** + * Creates the LockService bean. + * + * @param canonicalLogContextProvider the canonical log context provider + * @return the LockService instance + */ @Bean public LockService lockService(ObjectProvider canonicalLogContextProvider) { return new EtcdLockService(this.etcdClient, this.keyPrefix, this.maxRetries, this.requestTimeoutMs, diff --git a/locker-etcd/src/test/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockServiceActualTest.java b/locker-etcd/src/test/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockServiceActualTest.java index f00246e..551cd03 100644 --- a/locker-etcd/src/test/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockServiceActualTest.java +++ b/locker-etcd/src/test/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockServiceActualTest.java @@ -24,7 +24,7 @@ /** * The EtcdLockService test with actual etcd server. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Disabled diff --git a/locker-etcd/src/test/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockServiceTest.java b/locker-etcd/src/test/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockServiceTest.java index 692633b..604b33f 100644 --- a/locker-etcd/src/test/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockServiceTest.java +++ b/locker-etcd/src/test/java/com/unitvectory/lockservicecentral/locker/etcd/EtcdLockServiceTest.java @@ -33,7 +33,7 @@ /** * The EtcdLockService test with mocks. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ public class EtcdLockServiceTest { diff --git a/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockService.java b/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockService.java index 8e6e4b2..50d352d 100644 --- a/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockService.java +++ b/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockService.java @@ -31,7 +31,7 @@ /** * The Firestore LockService. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Slf4j @@ -43,7 +43,7 @@ public class FirestoreLockService implements LockService { /** * Constructs a new FirestoreLockService. - * + * * @param firestore the Firestore client * @param collectionLocks the collection name for locks * @param canonicalLogContextProvider provider for the canonical log context @@ -57,7 +57,7 @@ public FirestoreLockService(Firestore firestore, String collectionLocks, /** * Records the lock service outcome to the canonical log context. - * + * * @param outcome the screaming snake case outcome */ private void recordOutcome(String outcome) { diff --git a/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/LockerGcpConfig.java b/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/LockerGcpConfig.java index 9371a46..cda38ed 100644 --- a/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/LockerGcpConfig.java +++ b/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/LockerGcpConfig.java @@ -25,7 +25,7 @@ /** * The Configuration for the Firestore LockService. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @@ -37,6 +37,12 @@ public class LockerGcpConfig { @Value("${locker.firestore.collection:locks}") private String collectionLocks; + /** + * Creates the LockService bean. + * + * @param canonicalLogContextProvider the canonical log context provider + * @return the LockService instance + */ @Bean public LockService lockService(ObjectProvider canonicalLogContextProvider) { return new FirestoreLockService(this.firestore, this.collectionLocks, canonicalLogContextProvider); diff --git a/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/LockerGcpFirestoreConfig.java b/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/LockerGcpFirestoreConfig.java index 3ae21ad..5cd368f 100644 --- a/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/LockerGcpFirestoreConfig.java +++ b/locker-firestore/src/main/java/com/unitvectory/lockservicecentral/locker/firestore/LockerGcpFirestoreConfig.java @@ -22,7 +22,7 @@ /** * The Configuration for the Firestore LockService. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @@ -34,6 +34,11 @@ public class LockerGcpFirestoreConfig { @Value("${locker.firestore.database:(default)}") private String firestoreDatabase; + /** + * Creates the Firestore bean. + * + * @return the Firestore instance + */ @Bean public Firestore firestore() { FirestoreOptions.Builder builder = FirestoreOptions.getDefaultInstance().toBuilder() diff --git a/locker-firestore/src/test/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockServiceActualTest.java b/locker-firestore/src/test/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockServiceActualTest.java index eb98f3f..6dd3a07 100644 --- a/locker-firestore/src/test/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockServiceActualTest.java +++ b/locker-firestore/src/test/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockServiceActualTest.java @@ -23,7 +23,7 @@ /** * The FirestoreLockService test. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Disabled diff --git a/locker-firestore/src/test/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockServiceTest.java b/locker-firestore/src/test/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockServiceTest.java index 64aff0f..9f8362c 100644 --- a/locker-firestore/src/test/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockServiceTest.java +++ b/locker-firestore/src/test/java/com/unitvectory/lockservicecentral/locker/firestore/FirestoreLockServiceTest.java @@ -32,7 +32,7 @@ /** * The FirestoreLockService test. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ public class FirestoreLockServiceTest { diff --git a/locker-memory/src/main/java/com/unitvectory/lockservicecentral/locker/memory/LockerMemoryConfig.java b/locker-memory/src/main/java/com/unitvectory/lockservicecentral/locker/memory/LockerMemoryConfig.java index 953bd0e..424158b 100644 --- a/locker-memory/src/main/java/com/unitvectory/lockservicecentral/locker/memory/LockerMemoryConfig.java +++ b/locker-memory/src/main/java/com/unitvectory/lockservicecentral/locker/memory/LockerMemoryConfig.java @@ -22,12 +22,18 @@ /** * The Configuration for the Memory LockService. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration public class LockerMemoryConfig { + /** + * Creates the LockService bean. + * + * @param canonicalLogContextProvider the canonical log context provider + * @return the LockService instance + */ @Bean public LockService lockService(ObjectProvider canonicalLogContextProvider) { return new MemoryLockService(canonicalLogContextProvider); diff --git a/locker-memory/src/main/java/com/unitvectory/lockservicecentral/locker/memory/MemoryLockService.java b/locker-memory/src/main/java/com/unitvectory/lockservicecentral/locker/memory/MemoryLockService.java index 13d8987..84c6b03 100644 --- a/locker-memory/src/main/java/com/unitvectory/lockservicecentral/locker/memory/MemoryLockService.java +++ b/locker-memory/src/main/java/com/unitvectory/lockservicecentral/locker/memory/MemoryLockService.java @@ -26,7 +26,7 @@ /** * The Memory based LockService. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ public class MemoryLockService implements LockService { @@ -36,7 +36,7 @@ public class MemoryLockService implements LockService { /** * Constructs a new MemoryLockService. - * + * * @param canonicalLogContextProvider provider for the canonical log context */ public MemoryLockService(ObjectProvider canonicalLogContextProvider) { @@ -45,7 +45,7 @@ public MemoryLockService(ObjectProvider canonicalLogContext /** * Records the lock service outcome to the canonical log context. - * + * * @param outcome the screaming snake case outcome */ private void recordOutcome(String outcome) { @@ -83,7 +83,7 @@ public void clear() { /** * Get a lock by namespace and lock name. - * + * * @param namespace The lock namespace * @param lockName The lock name * @return The lock instance or null if it does not exist @@ -102,7 +102,7 @@ public Lock getLock(@NonNull String namespace, @NonNull String lockName) { /** * Acquire the lock if it's available. - * + * * @param lock The lock request * @param now The current timestamp * @return The lock response indicating success or failure @@ -138,7 +138,7 @@ public Lock acquireLock(@NonNull Lock lock, long now) { /** * Renew the lock by extending the lease duration. - * + * * @param lock The lock request * @param now The current timestamp * @return The renewed lock or failure if it cannot be renewed @@ -175,7 +175,7 @@ public Lock renewLock(@NonNull Lock lock, long now) { /** * Release the lock, making it available for others. - * + * * @param lock The lock request * @param now The current timestamp * @return The released lock or failure if it cannot be released @@ -209,7 +209,7 @@ public Lock releaseLock(@NonNull Lock lock, long now) { /** * Generate a unique key for the lock based on namespace and lock name. - * + * * @param namespace The lock namespace * @param lockName The lock name * @return A unique key for the lock diff --git a/locker-memory/src/test/java/com/unitvectory/lockservicecentral/locker/memory/MemoryLockServiceTest.java b/locker-memory/src/test/java/com/unitvectory/lockservicecentral/locker/memory/MemoryLockServiceTest.java index deec97a..d34c090 100644 --- a/locker-memory/src/test/java/com/unitvectory/lockservicecentral/locker/memory/MemoryLockServiceTest.java +++ b/locker-memory/src/test/java/com/unitvectory/lockservicecentral/locker/memory/MemoryLockServiceTest.java @@ -21,7 +21,7 @@ /** * The MemoryLockService test. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ public class MemoryLockServiceTest extends AbstractLockServiceTest { diff --git a/locker-tests/src/main/java/com/unitvectory/lockservicecentral/locker/tests/AbstractLockServiceTest.java b/locker-tests/src/main/java/com/unitvectory/lockservicecentral/locker/tests/AbstractLockServiceTest.java index 621cf4b..e57f6ef 100644 --- a/locker-tests/src/main/java/com/unitvectory/lockservicecentral/locker/tests/AbstractLockServiceTest.java +++ b/locker-tests/src/main/java/com/unitvectory/lockservicecentral/locker/tests/AbstractLockServiceTest.java @@ -31,13 +31,18 @@ /** * The set of tests the verify the behavior of a LockService. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ public abstract class AbstractLockServiceTest { private LockService lockService; + /** + * Creates and returns a LockService instance for testing. + * + * @return a new LockService instance + */ protected abstract LockService createLockService(); long getNow() { @@ -62,6 +67,13 @@ private void assertGetLockMatches(Lock actual, String namespace, String name, St assertEquals(expiry, actual.getExpiry(), "The getLock expiry does not match expected"); } + /** + * Asserts that an acquire lock operation has failed. + * + * @param requested the requested lock + * @param actual the actual lock returned + * @param now the current time in epoch seconds + */ public void assertAcquireLockFailed(Lock requested, Lock actual, long now) { // This is just testing to make sure the test is valid assertNotNull(requested, @@ -259,6 +271,9 @@ private void assertReleaseLockSuccess(Lock requested, Lock actual) { assertNull(actual.getExpiry(), "After release, expiry should be null"); } + /** + * Tests that getLock throws NullPointerException for null namespace. + */ @Test public void getLockNullNamespaceTest() { assertThrows(NullPointerException.class, () -> { @@ -266,6 +281,9 @@ public void getLockNullNamespaceTest() { }); } + /** + * Tests that getLock throws NullPointerException for null lockName. + */ @Test public void getLockNullLockNameTest() { assertThrows(NullPointerException.class, () -> { @@ -273,6 +291,9 @@ public void getLockNullLockNameTest() { }); } + /** + * Tests that acquireLock throws NullPointerException for null lock. + */ @Test public void acquireLockNullLockTest() { assertThrows(NullPointerException.class, () -> { @@ -280,6 +301,9 @@ public void acquireLockNullLockTest() { }); } + /** + * Tests that renewLock throws NullPointerException for null lock. + */ @Test public void renewLockNullLockTest() { assertThrows(NullPointerException.class, () -> { @@ -287,6 +311,9 @@ public void renewLockNullLockTest() { }); } + /** + * Tests that releaseLock throws NullPointerException for null lock. + */ @Test public void releaseLockNullLockTest() { assertThrows(NullPointerException.class, () -> { @@ -294,6 +321,9 @@ public void releaseLockNullLockTest() { }); } + /** + * Tests that getLock returns null for a non-existent lock. + */ @Test public void getLockNotFoundTest() { String name = UUID.randomUUID().toString(); @@ -301,6 +331,9 @@ public void getLockNotFoundTest() { assertNull(lock, "Non-existent lock should return null"); } + /** + * Tests that getLock returns the lock when it exists. + */ @Test public void getLockExistsTest() { // First we need to create a lock @@ -315,6 +348,9 @@ public void getLockExistsTest() { assertGetLockMatches(found, "junit", name, "owner", "instance", 60L, now + 60L); } + /** + * Tests sequential lock operations: acquire, renew, and release. + */ @Test public void sequentialOperationsTest() { String name = UUID.randomUUID().toString(); @@ -340,6 +376,9 @@ public void sequentialOperationsTest() { assertNull(finalLock, "Released lock should be deleted and return null"); } + /** + * Tests acquiring a lock with a past timestamp when another lock exists. + */ @Test public void acquireLockPastEdgeCaseTest() { String name = UUID.randomUUID().toString(); @@ -357,7 +396,9 @@ public void acquireLockPastEdgeCaseTest() { assertAcquireLockFailed(acquireLockPast, acquirePast, past); } - // Acquire a lock at the same time as an existing lock + /** + * Tests acquiring a lock at the same timestamp as an existing lock. + */ @Test public void acquireLockSameTimestampTest() { String name = UUID.randomUUID().toString(); @@ -375,6 +416,9 @@ public void acquireLockSameTimestampTest() { assertAcquireLockFailed(acquireLockSameTime, acquiredSameTime, now); } + /** + * Tests acquiring multiple independent locks. + */ @Test public void multipleLocksTest() { String name1 = UUID.randomUUID().toString(); @@ -399,6 +443,9 @@ public void multipleLocksTest() { assertGetLockMatches(found2, "junit", name2, "owner2", "instance2", 60L, now + 60); } + /** + * Tests acquiring a new lock. + */ @Test public void acquireLockNewTest() { String name = UUID.randomUUID().toString(); @@ -408,6 +455,9 @@ public void acquireLockNewTest() { assertAcquireLockSuccess(lock, acquired, now); } + /** + * Tests that acquiring an existing active lock fails. + */ @Test public void acquireLockExistingFailedTest() { // First we need to create a lock @@ -423,6 +473,9 @@ public void acquireLockExistingFailedTest() { assertAcquireLockFailed(lock2, acquired2, now); } + /** + * Tests acquiring an expired lock succeeds. + */ @Test public void acquireLockExistingExpiredTest() { // First we need to create a lock @@ -439,6 +492,9 @@ public void acquireLockExistingExpiredTest() { assertAcquireLockSuccess(lock2, acquired2, now); } + /** + * Tests reacquiring an existing lock by the same owner and instance succeeds. + */ @Test public void acquireLockExistingSuccessTest() { // First we need to create a lock @@ -455,6 +511,9 @@ public void acquireLockExistingSuccessTest() { assertAcquireLockSuccess(lock2, acquired2, now); } + /** + * Tests that renewing a non-existent lock fails. + */ @Test public void renewLockNotExistsTest() { // If we try to renew a lock that does not exist (by generating a random lock @@ -466,6 +525,9 @@ public void renewLockNotExistsTest() { assertRenewLockFailed(lock, renewed, now); } + /** + * Tests that renewing a lock with mismatched owner fails. + */ @Test public void renewLockNotMatchFailedTest() { // First we need to create a lock @@ -482,6 +544,9 @@ public void renewLockNotMatchFailedTest() { assertRenewLockFailed(lock2, renewed, now); } + /** + * Tests that renewing an existing lock succeeds. + */ @Test public void renewLockSuccessTest() { // First we need to create a lock @@ -498,6 +563,9 @@ public void renewLockSuccessTest() { assertRenewLockSuccess(lock2, renewed, now, 60L, now - 10 + 60); } + /** + * Tests that renewing an expired lock fails. + */ @Test public void renewLockExpiredFailed() { // First we need to create a lock @@ -514,6 +582,9 @@ public void renewLockExpiredFailed() { assertRenewLockFailed(lock2, renewed, now); } + /** + * Tests that releasing a non-existent lock succeeds. + */ @Test public void releaseLockNotExistsTest() { // If we try to release a lock that does not exist (by generating a random lock @@ -525,6 +596,9 @@ public void releaseLockNotExistsTest() { assertReleaseLockSuccess(lock, released); } + /** + * Tests that releasing a lock with mismatched owner fails. + */ @Test public void releaseLockNotMatchFailedTest() { // First we need to create a lock @@ -541,6 +615,9 @@ public void releaseLockNotMatchFailedTest() { assertReleaseLockFailed(lock2, released); } + /** + * Tests that releasing an expired lock succeeds. + */ @Test public void releaseLockExpiredTest() { // First we need to create a lock @@ -557,6 +634,9 @@ public void releaseLockExpiredTest() { assertReleaseLockSuccess(lock2, released); } + /** + * Tests that releasing an already released lock succeeds. + */ @Test public void releaseAlreadyReleasedLockTest() { String name = UUID.randomUUID().toString(); @@ -577,6 +657,9 @@ public void releaseAlreadyReleasedLockTest() { assertReleaseLockSuccess(releaseLock, releaseAgain); } + /** + * Tests that releasing an existing lock succeeds. + */ @Test public void releaseLockSuccess() { // First we need to create a lock diff --git a/locker/src/main/java/com/unitvectory/lockservicecentral/locker/Lock.java b/locker/src/main/java/com/unitvectory/lockservicecentral/locker/Lock.java index efaf9fa..cbe8e88 100644 --- a/locker/src/main/java/com/unitvectory/lockservicecentral/locker/Lock.java +++ b/locker/src/main/java/com/unitvectory/lockservicecentral/locker/Lock.java @@ -25,7 +25,7 @@ /** * The lock. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Data @@ -51,6 +51,11 @@ public class Lock { private Long expiry; + /** + * Constructs a Lock from a map of properties. + * + * @param map the map containing lock properties + */ public Lock(Map map) { this.namespace = (String) map.get("namespace"); this.lockName = (String) map.get("lockName"); @@ -60,11 +65,21 @@ public Lock(Map map) { this.expiry = (Long) map.get("expiry"); } + /** + * Creates a copy of this Lock. + * + * @return a new Lock instance with the same values + */ public Lock copy() { return new Lock(this.action, this.success, this.namespace, this.lockName, this.owner, this.instanceId, this.leaseDuration, this.expiry); } + /** + * Converts this Lock to a map representation. + * + * @return a map containing the lock properties + */ public Map toMap() { Map map = new HashMap<>(); map.put("namespace", this.namespace); @@ -76,6 +91,11 @@ public Map toMap() { return map; } + /** + * Sets the lock state for a GET operation. + * + * @param now the current time in epoch seconds + */ public void setGet(long now) { this.action = LockAction.GET; this.success = null; @@ -92,6 +112,9 @@ public void setGet(long now) { } } + /** + * Sets the lock state to indicate a failed operation. + */ public void setFailed() { this.action = LockAction.FAILED; this.success = false; @@ -101,11 +124,17 @@ public void setFailed() { this.expiry = null; } + /** + * Sets the lock state to indicate a successful operation. + */ public void setSuccess() { this.action = LockAction.SUCCESS; this.success = true; } + /** + * Sets the lock state to indicate the lock has been cleared. + */ public void setCleared() { this.action = LockAction.SUCCESS; this.success = true; @@ -115,14 +144,32 @@ public void setCleared() { this.expiry = null; } + /** + * Checks if the lock is currently active. + * + * @param now the current time in epoch seconds + * @return true if the lock is active, false otherwise + */ public boolean isActive(long now) { return this.expiry == null || this.expiry >= now; } + /** + * Checks if the lock has expired. + * + * @param now the current time in epoch seconds + * @return true if the lock has expired, false otherwise + */ public boolean isExpired(long now) { return this.expiry != null && this.expiry < now; } + /** + * Checks if this lock matches the given lock. + * + * @param lock the lock to compare against + * @return true if the locks match, false otherwise + */ public boolean isMatch(Lock lock) { if (lock == null) { return false; diff --git a/locker/src/main/java/com/unitvectory/lockservicecentral/locker/LockAction.java b/locker/src/main/java/com/unitvectory/lockservicecentral/locker/LockAction.java index 2bdc6f3..59f8ab5 100644 --- a/locker/src/main/java/com/unitvectory/lockservicecentral/locker/LockAction.java +++ b/locker/src/main/java/com/unitvectory/lockservicecentral/locker/LockAction.java @@ -15,7 +15,7 @@ /** * The lock action. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ public enum LockAction { diff --git a/locker/src/main/java/com/unitvectory/lockservicecentral/locker/LockService.java b/locker/src/main/java/com/unitvectory/lockservicecentral/locker/LockService.java index dfc06b1..d071bcd 100644 --- a/locker/src/main/java/com/unitvectory/lockservicecentral/locker/LockService.java +++ b/locker/src/main/java/com/unitvectory/lockservicecentral/locker/LockService.java @@ -15,14 +15,14 @@ /** * The LockService interface. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ public interface LockService { /** * Get a lock. - * + * * @param namespace the namespace * @param lockName the lock name * @return the lock @@ -31,7 +31,7 @@ public interface LockService { /** * Acquire a lock. - * + * * @param lock the lock request * @param now the current time * @return the lock response @@ -40,7 +40,7 @@ public interface LockService { /** * Renew a lock. - * + * * @param lock the lock request * @param now the current time * @return the lock response @@ -49,7 +49,7 @@ public interface LockService { /** * Release a lock. - * + * * @param lock the lock request * @param now the current time * @return the lock response diff --git a/locker/src/test/java/com/unitvectory/lockservicecentral/locker/LockTest.java b/locker/src/test/java/com/unitvectory/lockservicecentral/locker/LockTest.java index bfee38e..e2e8f55 100644 --- a/locker/src/test/java/com/unitvectory/lockservicecentral/locker/LockTest.java +++ b/locker/src/test/java/com/unitvectory/lockservicecentral/locker/LockTest.java @@ -21,7 +21,7 @@ /** * The Lock test. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ class LockTest { diff --git a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/AppRuntimeProperties.java b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/AppRuntimeProperties.java index 445c39b..a2c6b60 100644 --- a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/AppRuntimeProperties.java +++ b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/AppRuntimeProperties.java @@ -20,7 +20,7 @@ /** * Configuration properties for application runtime attributes used in canonical * logging. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Data diff --git a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalEmitInterceptor.java b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalEmitInterceptor.java index 9b6531f..b0403f2 100644 --- a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalEmitInterceptor.java +++ b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalEmitInterceptor.java @@ -31,12 +31,12 @@ /** * Interceptor that finalizes and emits the canonical log record exactly once. - * + * *

* In preHandle, captures the route template. In afterCompletion, computes final * baseline fields and emits the record. *

- * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Component @@ -48,7 +48,7 @@ public class CanonicalEmitInterceptor implements HandlerInterceptor { /** * Constructs a new interceptor. - * + * * @param contextProvider provides the request-scoped context * @param canonicalLogger the canonical logger * @param objectMapper the Jackson object mapper @@ -120,7 +120,7 @@ public void afterCompletion(HttpServletRequest request, HttpServletResponse resp /** * Determines the outcome based on status code and exception. - * + * * @param statusCode the HTTP status code * @param ex the exception, if any * @return the outcome string @@ -146,7 +146,7 @@ private String determineOutcome(int statusCode, Exception ex) { /** * Emits the canonical record as a JSON line. - * + * * @param context the canonical log context */ private void emitRecord(CanonicalLogContext context) { @@ -161,7 +161,7 @@ private void emitRecord(CanonicalLogContext context) { /** * Emits a minimal fallback record when normal emission fails. - * + * * @param cause the cause of the failure */ private void emitFallbackRecord(Exception cause) { diff --git a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalInitFilter.java b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalInitFilter.java index 11c1e6d..b633eee 100644 --- a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalInitFilter.java +++ b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalInitFilter.java @@ -34,13 +34,13 @@ /** * Servlet filter that initializes baseline fields in the canonical log context. - * + * *

* This filter runs early in the request lifecycle to capture request-start * fields * such as timestamps, request ID, HTTP method, and target path. *

- * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Component @@ -59,7 +59,7 @@ public class CanonicalInitFilter extends OncePerRequestFilter { /** * Constructs a new filter. - * + * * @param contextProvider provides the request-scoped context * @param appRuntimeProperties application runtime properties * @param buildProperties optional build properties @@ -128,7 +128,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse /** * Resolves the client IP address from X-Forwarded-For header or remote address. - * + * * @param request the HTTP request * @return the client IP address */ diff --git a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLogContext.java b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLogContext.java index f12efd7..301c031 100644 --- a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLogContext.java +++ b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLogContext.java @@ -30,7 +30,7 @@ /** * Request-scoped context for building a canonical log record. - * + * *

* Each HTTP request gets an isolated instance. Controllers and services can add * fields @@ -38,16 +38,16 @@ * record is emitted * exactly once at request completion. *

- * + * *

* Thread-safe for limited intra-request parallelism when explicitly propagated. *

- * + * *

* Note: This class is configured as request-scoped with a scoped proxy in * {@link CanonicalLoggingConfig}. *

- * + * * @author Jared Hatfield (UnitVectorY Labs) */ public class CanonicalLogContext { @@ -95,14 +95,14 @@ public CanonicalLogContext() { /** * Adds a field to the canonical record. - * + * *

* Silently ignores null values. Validates that the key is snake_case and the * value * is a supported type. Invalid keys or values are logged as warnings but do not * throw. *

- * + * * @param key the field name (must be snake_case) * @param value the field value (must be a supported type) */ @@ -130,7 +130,7 @@ public void put(String key, Object value) { * of * the input. This avoids logging the exact value while still allowing * correlation. - * + * * @param key the field name (must be snake_case) * @param value the field value to hash */ @@ -149,7 +149,7 @@ public void putSHA256(String key, String value) { /** * Normalizes a value to a supported type for flat JSON serialization. - * + * * @param key the field key (for logging) * @param value the value to normalize * @return the normalized value, or null if the type is not supported @@ -175,7 +175,7 @@ private Object normalizeValue(String key, Object value) { /** * Returns a defensive copy of the current fields. - * + * * @return a snapshot of the fields */ public Map snapshot() { @@ -186,7 +186,7 @@ public Map snapshot() { /** * Marks this record as emitted if it hasn't been already. - * + * * @return true if this was the first call (emission should proceed), false * otherwise */ @@ -196,7 +196,7 @@ public boolean markEmittedIfFirst() { /** * Returns whether the record has been emitted. - * + * * @return true if already emitted */ public boolean isEmitted() { @@ -205,7 +205,7 @@ public boolean isEmitted() { /** * Truncates a string value to the specified maximum length. - * + * * @param value the value to truncate * @param maxLength the maximum length * @return the truncated value @@ -222,7 +222,7 @@ public static String truncate(String value, int maxLength) { /** * Truncates an error message to the standard maximum length. - * + * * @param message the message to truncate * @return the truncated message */ @@ -232,7 +232,7 @@ public static String truncateErrorMessage(String message) { /** * Truncates a user agent string to the standard maximum length. - * + * * @param userAgent the user agent to truncate * @return the truncated user agent */ @@ -242,7 +242,7 @@ public static String truncateUserAgent(String userAgent) { /** * Converts an exception to a stack trace string. - * + * * @param ex the exception * @return the stack trace as a string */ diff --git a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLogger.java b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLogger.java index 5c9dd33..1173998 100644 --- a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLogger.java +++ b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLogger.java @@ -19,12 +19,12 @@ /** * Dedicated logger for canonical log records. - * + * *

* Wraps a SLF4J logger named "canonical" to keep the intent clear and ensure * canonical records are routed to a dedicated appender. *

- * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Component @@ -34,7 +34,7 @@ class CanonicalLogger { /** * Logs a canonical JSON line at INFO level. - * + * * @param jsonLine the JSON log line */ void info(String jsonLine) { @@ -43,7 +43,7 @@ void info(String jsonLine) { /** * Logs a warning message. - * + * * @param message the warning message */ void warn(String message) { @@ -52,7 +52,7 @@ void warn(String message) { /** * Logs an error message. - * + * * @param message the error message * @param ex the exception */ diff --git a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLoggingConfig.java b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLoggingConfig.java index e6db278..551d770 100644 --- a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLoggingConfig.java +++ b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/CanonicalLoggingConfig.java @@ -22,7 +22,7 @@ /** * Spring configuration for canonical logging components. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ @Configuration @@ -31,12 +31,12 @@ public class CanonicalLoggingConfig { /** * Creates a request-scoped canonical log context with a scoped proxy. - * + * *

* The scoped proxy ensures that singleton beans (like filters and interceptors) * can safely inject this request-scoped bean without causing scope issues. *

- * + * * @return a new context for each request */ @Bean diff --git a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/HashUtil.java b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/HashUtil.java index d6ba403..6dacfc3 100644 --- a/logging/src/main/java/com/unitvectory/lockservicecentral/logging/HashUtil.java +++ b/logging/src/main/java/com/unitvectory/lockservicecentral/logging/HashUtil.java @@ -19,12 +19,12 @@ /** * Utility class for hashing sensitive values before logging. - * + * *

* This is useful when you need to log an identifier for correlation purposes * but the raw value should not be exposed in logs (e.g., instance IDs, tokens). *

- * + * * @author Jared Hatfield (UnitVectorY Labs) */ final class HashUtil { @@ -35,7 +35,7 @@ private HashUtil() { /** * Computes the SHA-256 hex digest of a string. - * + * * @param input the input string * @return the hex digest, or "hash_error" if hashing fails */ diff --git a/logging/src/test/java/com/unitvectory/lockservicecentral/logging/CanonicalEmitInterceptorTest.java b/logging/src/test/java/com/unitvectory/lockservicecentral/logging/CanonicalEmitInterceptorTest.java index 97dabb9..6c1718e 100644 --- a/logging/src/test/java/com/unitvectory/lockservicecentral/logging/CanonicalEmitInterceptorTest.java +++ b/logging/src/test/java/com/unitvectory/lockservicecentral/logging/CanonicalEmitInterceptorTest.java @@ -32,7 +32,7 @@ /** * Unit tests for CanonicalEmitInterceptor. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ class CanonicalEmitInterceptorTest { diff --git a/logging/src/test/java/com/unitvectory/lockservicecentral/logging/CanonicalLogContextTest.java b/logging/src/test/java/com/unitvectory/lockservicecentral/logging/CanonicalLogContextTest.java index 2b48401..205ef7c 100644 --- a/logging/src/test/java/com/unitvectory/lockservicecentral/logging/CanonicalLogContextTest.java +++ b/logging/src/test/java/com/unitvectory/lockservicecentral/logging/CanonicalLogContextTest.java @@ -24,7 +24,7 @@ /** * Unit tests for CanonicalLogContext. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ class CanonicalLogContextTest { diff --git a/logging/src/test/java/com/unitvectory/lockservicecentral/logging/HashUtilTest.java b/logging/src/test/java/com/unitvectory/lockservicecentral/logging/HashUtilTest.java index b6aa6c2..4bb2605 100644 --- a/logging/src/test/java/com/unitvectory/lockservicecentral/logging/HashUtilTest.java +++ b/logging/src/test/java/com/unitvectory/lockservicecentral/logging/HashUtilTest.java @@ -19,7 +19,7 @@ /** * Unit tests for HashUtil. - * + * * @author Jared Hatfield (UnitVectorY Labs) */ class HashUtilTest {