Class ActivationService
ActivationRequest state transition and every
ActivationState write. No other class changes either.
The activation lives in the plugin store, outside the job's config.xml, so none of
the configuration write paths (web form, REST, CLI, script, JCasC) can activate a job. It is
changed only by an approved ACTIVATE/HOLD request and, once, by the upgrade
seeding (seedExistingJobs()).
Concurrency: as in RunRequestService, every load, validate, transition and
persist sequence runs under one ReentrantLock, so a transition is an effective
compare-and-set. isActivated(Job) takes no lock: the queue gate calls it while Jenkins
holds the queue lock, and it is answered from an in-memory cache (filled from the store on first
use per job and kept in step by every write here).
Failure families: IllegalArgumentException for input validation,
IllegalStateException for wrong-state transitions, AccessDeniedException (403)
for authorization refusals.
-
Method Summary
Modifier and TypeMethodDescriptionstatic ItemactivationSubject(Item item) The item whose activation governsitem(D-46c, D-82): the item itself, or for a computed child its nearest computed-folder ancestor, or for a job's sub-item (a matrix configuration, a Maven module) its parent job, resolved again while the item reached is itself such a child (a repository project of an organization folder is carried by the organization folder, which is the item that was created and can be requested on; a configuration of a matrix project inside a multibranch project by that project).voidEnds, as INVALIDATED, every PENDING request that missed an invalidation while its file could not be read or written (invalidatePending(java.lang.String, boolean, java.lang.String)) and can now be, when the invalidation applies to the job it names; forgets the others.Approves a PENDING request and applies it:ACTIVATEmarks the job activated and writes anChangeType.ACTIVATEDrecord,HOLDmarks it not activated and writes aChangeType.HELDrecord.booleancanCancel(ActivationRequest request) D-38b: whether the current user may cancelrequest: its requester holdingBatchControl/Requeston the item, or a Manage holder.Cancels a PENDING request; requester or a Manage holder only (SPEC item 7 rule).booleancanChangeApprovers(ActivationRequest request) D-38b: whether the current user may change the approvers ofrequest: its requester holdingBatchControl/Requeston the item.booleancanRequest(Item item) D-38b: whether the current user could request an activation or hold ofitem: the permission checks ofcreate(Item, ActivationRequest.Action, String, List), namelyBatchControl/RequestandItem/Readon the item.changeApprovers(String id, List<String> newApprovers) Replaces the designated approver set of a PENDING request; requester only (D-26, D-37).countPendingFor(org.springframework.security.core.Authentication auth) D-61: the PENDING activation and hold requests that concernauth, read from the open-request index only (no history scan): those awaiting their decision as a designated approver holding Jenkins-levelBatchControl/Approve, else their own.Creates a PENDING activation or hold request for the job.Job form ofcreate(Item, ActivationRequest.Action, String, List).voidExpires PENDING requests pastpendingTimeoutHours, the run-request timeout.static ActivationServiceget()The stored activation state of an item (a job or a computed folder), ornull.Job form ofgetState(Item).The stored activation state of an item, ornullif none is stored.static Job<?, ?> governingJob(Job<?, ?> job) D-82: the job whose approval property governsjobat the queue gate: the job itself, or for a sub-item the topmost job among its parents (a configuration's matrix project).booleanhasOwnRequests(org.springframework.security.core.Authentication auth) D-38b: whetherauthfiled at least one activation or hold request, in any status (the requester always sees their own, P-09).booleanholdAfterMove(Item item, String oldFullName, String mover) D-59a (SPEC 6a):itemwas moved fromoldFullNamebymover, a user without Overall/Administer, while run control and change control were on.e2e-04 FD-07, security-25 S-25-03: the part of a refusal record's coalescing key that changes when the item's activation ends:never-activated,held-<epoch millis>, orunknownwhen the state cannot be read.booleanisActivated(Item item) Whether an approved activation is stored for this very item.booleanisActivated(Job<?, ?> job) Job form ofisActivated(Item).static booleanisComputedChild(Item item) D-32: a job its parent computes for itself (a multibranch branch job, an organization folder's repositories) is not controlled and needs no activation.static booleanD-82: whetheritemis a sub-item of a job (a multi-configuration project's configuration, a Maven module): its parent is itself a job.static booleanWhether the item carries an activation of its own: a job or a computed folder that is not itself a computed child (D-46) nor a job's sub-item (D-82).list()All stored activation requests, in creation order.listAwaitingDecision(org.springframework.security.core.Authentication auth) The PENDING requests counted asPendingCount.getAwaitingDecision()bycountPendingFor(Authentication), in creation order (oldest, the one closest to its timeout, first): empty unlessauthholds Jenkins-levelBatchControl/Approve.The PENDING activation and hold requests, in creation order (the approval inbox).listPendingFor(String userId) The PENDING requests on whichuserIdis a designated approver.listPendingForJob(String jobFullName) The PENDING requests of one job, in creation order.Loads an activation request by id, ornull.booleanmayRunUnattended(Job<?, ?> job) The queue gate's activation input (SPEC item 6a, D-46): whether the job may start on a cause that is not a human submission.voidD-36: sendsNotificationEvent.EXPIRINGonce per PENDING request close to its timeout.voidonItemCreated(Item item) An item was created (SPEC item 6a, D-45, D-46).Rejects a PENDING request; the comment is mandatory (SPEC item 5 rule).voidA job was renamed or moved: its activation follows it, and PENDING requests on the old name end INVALIDATED, as run requests do (D-21: the approver reviewed another identity).voidA job was deleted: its activation is removed and its PENDING requests end INVALIDATED, so a later job of the same name starts not activated and cannot be activated by a request that was about another job.intOne-time upgrade seeding: whenactivations/.schemais absent, every job that exists now (other than a computed child, D-32) is recorded as activated withactivatedBy = upgradeand oneChangeType.ACTIVATEDrecord, and then the marker is written.
-
Method Details
-
get
-
isActivated
Whether an approved activation is stored for this very item. The answer is truthful: a computed child, which nobody activates, reports not activated; whether it may run ismayRunUnattended(Job), which asks its computed-folder ancestor (D-46c).Fails closed: a state that cannot be read, one bound to another directory than the item's current one (a re-created item under an old name, S-13-09), or one whose directory marker is missing or cannot be read now (D-80), counts as not activated.
-
holdEpoch
e2e-04 FD-07, security-25 S-25-03: the part of a refusal record's coalescing key that changes when the item's activation ends:never-activated,held-<epoch millis>, orunknownwhen the state cannot be read. Answered from the activation cache thatisActivated(Item)fills, so the queue gate does not read the disk; never throws. -
isActivated
Job form ofisActivated(Item). -
mayRunUnattended
The queue gate's activation input (SPEC item 6a, D-46): whether the job may start on a cause that is not a human submission. A computed child asks the item that carries its activation (activationSubject(hudson.model.Item)); any other job asks itself. -
activationSubject
The item whose activation governsitem(D-46c, D-82): the item itself, or for a computed child its nearest computed-folder ancestor, or for a job's sub-item (a matrix configuration, a Maven module) its parent job, resolved again while the item reached is itself such a child (a repository project of an organization folder is carried by the organization folder, which is the item that was created and can be requested on; a configuration of a matrix project inside a multibranch project by that project). -
isSubject
Whether the item carries an activation of its own: a job or a computed folder that is not itself a computed child (D-46) nor a job's sub-item (D-82). -
isSubItem
D-82: whetheritemis a sub-item of a job (a multi-configuration project's configuration, a Maven module): its parent is itself a job. It has no activation or approval state of its own; the queue gate applies its parent job's (governingJob(hudson.model.Job<?, ?>)). -
governingJob
D-82: the job whose approval property governsjobat the queue gate: the job itself, or for a sub-item the topmost job among its parents (a configuration's matrix project). -
getState
The stored activation state of an item, ornullif none is stored. -
getState
The stored activation state of an item (a job or a computed folder), ornull. -
getState
Job form ofgetState(Item). -
load
Loads an activation request by id, ornull. -
list
All stored activation requests, in creation order. -
hasOwnRequests
public boolean hasOwnRequests(org.springframework.security.core.Authentication auth) D-38b: whetherauthfiled at least one activation or hold request, in any status (the requester always sees their own, P-09). Answered from the store's in-memory index; no item is visited. -
canRequest
D-38b: whether the current user could request an activation or hold ofitem: the permission checks ofcreate(Item, ActivationRequest.Action, String, List), namelyBatchControl/RequestandItem/Readon the item. Screens use it to show the request form;createstill checks for real. -
canCancel
D-38b: whether the current user may cancelrequest: its requester holdingBatchControl/Requeston the item, or a Manage holder. Permission only. -
canChangeApprovers
D-38b: whether the current user may change the approvers ofrequest: its requester holdingBatchControl/Requeston the item. Permission only. -
listPending
The PENDING activation and hold requests, in creation order (the approval inbox). -
listPendingFor
The PENDING requests on whichuserIdis a designated approver. -
countPendingFor
D-61: the PENDING activation and hold requests that concernauth, read from the open-request index only (no history scan): those awaiting their decision as a designated approver holding Jenkins-levelBatchControl/Approve, else their own. Every counted request is visible toauthunder P-09. The single source for the tab badge and the section. -
listAwaitingDecision
public List<ActivationRequest> listAwaitingDecision(org.springframework.security.core.Authentication auth) The PENDING requests counted asPendingCount.getAwaitingDecision()bycountPendingFor(Authentication), in creation order (oldest, the one closest to its timeout, first): empty unlessauthholds Jenkins-levelBatchControl/Approve. -
listPendingForJob
The PENDING requests of one job, in creation order. -
create
public ActivationRequest create(Job<?, ?> job, ActivationRequest.Action action, String reason, List<String> approvers) Job form ofcreate(Item, ActivationRequest.Action, String, List). -
create
public ActivationRequest create(Item job, ActivationRequest.Action action, String reason, List<String> approvers) Creates a PENDING activation or hold request for the job. The requester is the current authentication and needsBatchControl/RequestplusItem/Readon the job (D-39). Validation as for run requests: a reason of at most 4000 characters and a designated approver set that passes the SPEC item 3 rules, including the job's own approver list (D-37).ACTIVATEis refused for a job that is already activated andHOLDfor a job that is not, since neither could change anything. A computed child (D-32) is not controlled and cannot be the subject of a request. -
approve
Approves a PENDING request and applies it:ACTIVATEmarks the job activated and writes anChangeType.ACTIVATEDrecord,HOLDmarks it not activated and writes aChangeType.HELDrecord. The caller must be a designated approver holdingBatchControl/Approve(or the administrator self-approval path), exactly as for a run request. A request past its pending timeout is expired instead; a request whose job no longer exists is invalidated. -
reject
Rejects a PENDING request; the comment is mandatory (SPEC item 5 rule). Nothing else changes. -
cancel
Cancels a PENDING request; requester or a Manage holder only (SPEC item 7 rule). -
changeApprovers
Replaces the designated approver set of a PENDING request; requester only (D-26, D-37). Recorded as (previous set, new set, changed by, time). -
expireOverduePending
public void expireOverduePending()Expires PENDING requests pastpendingTimeoutHours, the run-request timeout. Called by the periodic work; the truth stays the clock comparison (approve(java.lang.String, java.lang.String)expires a late request on its own). -
notifyExpiring
public void notifyExpiring()D-36: sendsNotificationEvent.EXPIRINGonce per PENDING request close to its timeout. Each request is handled on its own: one that cannot be read or written is logged and tried again on the next run, and never keeps the others from their notice. -
relocate
A job was renamed or moved: its activation follows it, and PENDING requests on the old name end INVALIDATED, as run requests do (D-21: the approver reviewed another identity). Runs regardless of the switches; it is bookkeeping, not control. -
holdAfterMove
D-59a (SPEC 6a):itemwas moved fromoldFullNamebymover, a user without Overall/Administer, while run control and change control were on. It starts over like a newly created item: it is no longer activated, and aChangeType.HELDrecord naming the move and the mover is written. Called after the move completed (the state has already followed the item throughrelocate(java.lang.String, java.lang.String)). A computed child or a job's sub-item carries no activation of its own (D-46, D-82) and is skipped; a computed folder, which carries its children's, is held. The D-34 configuration lock is applied by the caller. The caller checks the switches.- Returns:
- whether the item was handled (a job or computed folder that is not a computed child)
-
remove
A job was deleted: its activation is removed and its PENDING requests end INVALIDATED, so a later job of the same name starts not activated and cannot be activated by a request that was about another job. For a deleted folder the same applies to everything stored below it. -
onItemCreated
An item was created (SPEC item 6a, D-45, D-46). For an item that carries its own activation (a job or a computed folder that is not a computed child):- run control on: an explicit not-activated state is stored (S-13-06), so a retried seeding never activates it and nothing left under the name is inherited (S-13-04);
- run control off: it is recorded as activated by
ActivationState.UNCONTROLLEDwith anChangeType.ACTIVATEDrecord (S-13-10), so turning run control on later never stops a schedule created in between.
-
seedExistingJobs
public int seedExistingJobs()One-time upgrade seeding: whenactivations/.schemais absent, every job that exists now (other than a computed child, D-32) is recorded as activated withactivatedBy = upgradeand oneChangeType.ACTIVATEDrecord, and then the marker is written. With the marker present this does nothing, so it never re-seeds, not even after the activation files are removed. A job that already has a stored state is left alone, so a seeding interrupted before the marker was written completes without duplicates.Seeding does not depend on the switches: installing with run control off and turning it on later must not stop an existing schedule either. With run control off the state has no effect.
Must run with every job visible: it is called from an initializer, which runs as SYSTEM.
- Returns:
- the number of jobs seeded, or
-1when the marker was already present
-
isComputedChild
D-32: a job its parent computes for itself (a multibranch branch job, an organization folder's repositories) is not controlled and needs no activation. Same test as the D-31/D-34 default inlistener.ItemChangeListener. -
applyMissedInvalidations
public void applyMissedInvalidations()Ends, as INVALIDATED, every PENDING request that missed an invalidation while its file could not be read or written (invalidatePending(java.lang.String, boolean, java.lang.String)) and can now be, when the invalidation applies to the job it names; forgets the others. Run by the periodic work every minute; a request that still cannot be read stays for the next run.
-