Class UsageLockValidator
- All Implemented Interfaces:
CommandValidator,PlayerCacheSaver
Acquire-then-execute (D-02, GEN-09, @since 6.3.0): acquisition is no longer a
separate step called by field from BaseCommandExecutor.executeCommand. It happens
inside validate(CommandContext) itself -- "acquire-as-you-validate" -- so that on
the synchronous dispatch path there is no scheduling point between deciding the lock is free
and taking it. A failed acquisition surfaces as an ordinary CommandValidator.ValidationResult.failure(java.lang.String)
through the normal validation-rejection path; the mapped method is never invoked. Release
happens in onComplete(CommandContext, boolean), driven by the validator chain that
actually ran this validator (see CommandValidator.onComplete(CommandContext, boolean)) --
never called twice, and never called for an invocation whose acquisition failed -- or, when the
dispatch is refused after this validator acquired the lock but before the method runs, in
onRefused(CommandContext) (#568).
Player-cache lifecycle (case 48, @since 6.3.0): an instance registers itself with PlayerCacheManager at its first validate(CommandContext), so quits and the periodic
sweep reach its per-player state. It is released when the last executor that has dispatched
through it unloads -- BaseCommandExecutor's teardown decides that and calls releasePlayerCacheRegistration() -- and it registers again on its next use, so a chain reused
after a release keeps being swept.
- Since:
- 6.2.0
- Version:
- 2.0.0
- Author:
- wisdomme
-
Nested Class Summary
Nested classes/interfaces inherited from interface com.ultikits.ultitools.abstracts.command.validation.CommandValidator
CommandValidator.ValidationResult -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final intprivate booleanTrue once this instance has registeredsenderLocks/serverLockswith the livePlayerCacheManagerfor quit-based sweeping (GEN-08, D-03).Locks per sender: player UUID -> set of locked method keysServer-wide locks: method key -> locking player UUID -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionbooleanacquireLock(CommandContext context) Acquires a lock for command execution.voidClears all locks.voidclearPlayerLocks(UUID playerId) Clears all locks for a player.private voidAttempts lazy first-use registration of this instance with the livePlayerCacheManagersingleton.getName()Gets the name of this validator for debugging and logging purposes.intgetOrder()Gets the order of this validator.booleanChecks if a specific method is locked for a player.voidonComplete(CommandContext context, boolean commandSucceeded) Post-action hook that releases the lock acquired byvalidate(CommandContext)for this invocation.voidonRefused(CommandContext context) Releases the lock this validator acquired for a dispatch that was then refused before the mapped method ran -- by a later validator (such as the cooldown, which runs after this one), the argument-count check, or parameter parsing.voidreleaseLock(CommandContext context) Releases a lock after command execution.final voidStopsPlayerCacheManagertracking of this instance and re-arms its lazy registration, so the nextvalidate(CommandContext)registers it again (case 48).private static UsageLimitresolveLimit(Method method, Class<?> executorClass) Resolves the effective@UsageLimitformethod: the method's own declaration, falling back to a class-level one on the CONCRETE executor class dispatching this command, falling back to a class-level one on the method's declaring class -- most-derived-wins, viaReflectionUtil.resolveMethodOrClassAnnotation(Method, Class, Class).voidsavePlayerData(UUID playerId) PlayerCacheSaverhook: fired byPlayerCacheManager.onPlayerQuit(UUID)("the quit sweep") for the quitting player -- before the generic@PlayerCachefield sweep removes the (by then already-empty)senderLocks/serverLocksentries.validate(CommandContext context) Validates -- and, per the acquire-then-execute contract on this class, ATTEMPTS TO ACQUIRE -- the lock for this invocation.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface com.ultikits.ultitools.abstracts.command.validation.CommandValidator
shouldValidate
-
Field Details
-
ORDER
private static final int ORDER- See Also:
-
senderLocks
Locks per sender: player UUID -> set of locked method keyssaveBeforeRemove = truesosavePlayerData(UUID)-- which delegates to the pre-existingclearPlayerLocks(UUID), clearing BOTH this field andserverLocksin one call -- fires exactly once per quit (GEN-08, D-03).serverLocksdeliberately does NOT also carrysaveBeforeRemove = true: doing so would firesavePlayerData(UUID)a second, redundant time per quit. -
serverLocks
Server-wide locks: method key -> locking player UUID -
playerCacheRegistered
private volatile boolean playerCacheRegisteredTrue once this instance has registeredsenderLocks/serverLockswith the livePlayerCacheManagerfor quit-based sweeping (GEN-08, D-03). Set lazily fromvalidate(CommandContext)rather than the constructor, mirroringCooldownValidator's identical field of the same name and rationale: a barenew UsageLockValidator()must never attempt contact with a core plugin that may not exist yet, and a failed attempt is retried on the next call rather than permanently abandoned.Cleared by
releasePlayerCacheRegistration()when the last executor that has dispatched through this instance unloads, so a reused instance registers again on its nextvalidate(CommandContext)instead of staying unswept (case 48).
-
-
Constructor Details
-
UsageLockValidator
public UsageLockValidator()
-
-
Method Details
-
validate
Validates -- and, per the acquire-then-execute contract on this class, ATTEMPTS TO ACQUIRE -- the lock for this invocation. Delegates entirely toacquireLock(CommandContext): a successful acquisition is a successful validation; a failed acquisition is reported as an ordinary validation failure carrying the scope-appropriate i18n key.- Specified by:
validatein interfaceCommandValidator- Parameters:
context- the command context- Returns:
- the validation result
-
onComplete
Post-action hook that releases the lock acquired byvalidate(CommandContext)for this invocation. Delegates toreleaseLock(CommandContext)and is invoked only by a chain that actually ran this validator for the current dispatch -- seeCommandValidator.onComplete(CommandContext, boolean). An invocation whose acquisition failed never reaches this hook (it is absent from the chain's passed-validator list), so there is nothing here to release on that path.- Specified by:
onCompletein interfaceCommandValidator- Parameters:
context- the command contextcommandSucceeded- ignored -- the lock releases whether the mapped method succeeded or threw- Since:
- 6.3.0
-
onRefused
Releases the lock this validator acquired for a dispatch that was then refused before the mapped method ran -- by a later validator (such as the cooldown, which runs after this one), the argument-count check, or parameter parsing. Before 6.3.0 nothing released it on those paths, and the lock stayed held until the player quit (#568).- Specified by:
onRefusedin interfaceCommandValidator- Parameters:
context- the command context- Since:
- 6.3.0
-
ensurePlayerCacheRegistered
private void ensurePlayerCacheRegistered()Attempts lazy first-use registration of this instance with the livePlayerCacheManagersingleton. Safe to call unconditionally on everyvalidate(CommandContext)invocation: a no-op onceplayerCacheRegisteredis true, and a cheap, safely-no-op-on-failure retry otherwise (see that field's javadoc). -
releasePlayerCacheRegistration
@Internal public final void releasePlayerCacheRegistration()StopsPlayerCacheManagertracking of this instance and re-arms its lazy registration, so the nextvalidate(CommandContext)registers it again (case 48).Called by
BaseCommandExecutor's unload teardown once no other loaded executor that has dispatched still uses this validator. Not intended for module code: releasing a validator that a loaded executor still uses stops the quit sweep from reaching it until its next dispatch; the locks it holds are left as they are (clearAllLocks()is the teardown's separate step).- Since:
- 6.3.0
-
savePlayerData
PlayerCacheSaverhook: fired byPlayerCacheManager.onPlayerQuit(UUID)("the quit sweep") for the quitting player -- before the generic@PlayerCachefield sweep removes the (by then already-empty)senderLocks/serverLocksentries. Delegates to the pre-existingclearPlayerLocks(UUID)rather than re-deriving its predicate -- the SAME predicatePlayerCacheManager's own value-side sweep branch was built from -- giving that previously zero-caller method a real, quit-path-reached production call site (GEN-08). OnlysenderLockscarriessaveBeforeRemove = true, so this fires exactly once per quit despite clearing both maps.- Specified by:
savePlayerDatain interfacePlayerCacheSaver- Parameters:
playerId- the UUID of the player quitting- Since:
- 6.3.0
-
acquireLock
Acquires a lock for command execution. Should be called before executing the command. As of 6.3.0 this is also the methodvalidate(CommandContext)itself calls -- see the class-level acquire-then-execute note.- Parameters:
context- the command context- Returns:
- true if lock was acquired, false if already locked
-
releaseLock
Releases a lock after command execution. Should be called after command execution completes (success or failure). As of 6.3.0 this is also the methodonComplete(CommandContext, boolean)calls -- see the class-level acquire-then-execute note.- Parameters:
context- the command context
-
resolveLimit
Resolves the effective@UsageLimitformethod: the method's own declaration, falling back to a class-level one on the CONCRETE executor class dispatching this command, falling back to a class-level one on the method's declaring class -- most-derived-wins, viaReflectionUtil.resolveMethodOrClassAnnotation(Method, Class, Class). This is the SAME resolutionPluginManager's load-time refusal treats as satisfying the contract (SILENT-11 / D-01 follow-up, WR-02 / 05-REVIEW.md fix): a class-level@UsageLimitthat passes the load-time check -- whether declared on a shared abstract base or on the concrete executor class itself -- now actually locks every inherited mapping that does not declare its own.- Parameters:
method- the matched command mapping method, ornullexecutorClass- the concrete executor class dispatching this command (WR-02, 05-REVIEW.md), ornullwhen unavailable -- falls back to the pre-WR-02, declaring-class-only resolution in that case- Returns:
- the resolved annotation, or
nullwhenmethodisnullor neither the method,executorClass, nor its declaring class carries one - Since:
- 6.3.0
-
clearPlayerLocks
Clears all locks for a player. Useful when a player disconnects.- Parameters:
playerId- the player's UUID
-
clearAllLocks
public void clearAllLocks()Clears all locks. -
isLocked
Checks if a specific method is locked for a player.- Parameters:
playerId- the player's UUIDmethodKey- the method key- Returns:
- true if locked
-
getOrder
public int getOrder()Description copied from interface:CommandValidatorGets the order of this validator. Lower values are executed first.- Specified by:
getOrderin interfaceCommandValidator- Returns:
- the order value
-
getName
Description copied from interface:CommandValidatorGets the name of this validator for debugging and logging purposes.- Specified by:
getNamein interfaceCommandValidator- Returns:
- the validator name
-