From 766475ae9a1d8f060eb4a2c55f8854bc70751499 Mon Sep 17 00:00:00 2001 From: joe miller Date: Mon, 25 Feb 2013 14:10:13 -0800 Subject: [PATCH 01/22] basic support for limiting concurrent job runs by parameters --- pom.xml | 2 +- .../ThrottleJobProperty.java | 9 ++- .../ThrottleQueueTaskDispatcher.java | 67 ++++++++++++++++++- .../throttleconcurrents/Messages.properties | 3 +- .../ThrottleJobProperty/config.jelly | 5 ++ .../help-limitOneJobWithMatchingParams.html | 4 ++ 6 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/help-limitOneJobWithMatchingParams.html diff --git a/pom.xml b/pom.xml index edef7334..a8c810c4 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ THE SOFTWARE. org.jenkins-ci.plugins plugin - 1.399 + 1.500 throttle-concurrents diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java index 572c8e5c..6d21de77 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java @@ -34,6 +34,7 @@ public class ThrottleJobProperty extends JobProperty> { private List categories; private boolean throttleEnabled; private String throttleOption; + private boolean limitOneJobWithMatchingParams; /** * Store a config version so we're able to migrate config on various @@ -46,12 +47,14 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode, Integer maxConcurrentTotal, List categories, boolean throttleEnabled, - String throttleOption) { + String throttleOption, + boolean limitOneJobWithMatchingParams) { this.maxConcurrentPerNode = maxConcurrentPerNode == null ? 0 : maxConcurrentPerNode; this.maxConcurrentTotal = maxConcurrentTotal == null ? 0 : maxConcurrentTotal; this.categories = categories; this.throttleEnabled = throttleEnabled; this.throttleOption = throttleOption; + this.limitOneJobWithMatchingParams = limitOneJobWithMatchingParams; } @@ -89,6 +92,10 @@ public boolean getThrottleEnabled() { return throttleEnabled; } + public boolean getlimitOneJobWithMatchingParams() { + return limitOneJobWithMatchingParams; + } + public String getThrottleOption() { return throttleOption; } diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index d52682a3..6f59b06a 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -5,14 +5,18 @@ import hudson.matrix.MatrixProject; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; +import hudson.model.ParameterValue; import hudson.model.Computer; import hudson.model.Executor; import hudson.model.Hudson; import hudson.model.Node; import hudson.model.Queue; import hudson.model.Queue.Task; +import hudson.model.queue.WorkUnit; import hudson.model.queue.CauseOfBlockage; import hudson.model.queue.QueueTaskDispatcher; +import hudson.model.Action; +import hudson.model.ParametersAction; import java.util.ArrayList; import java.util.HashMap; @@ -35,7 +39,6 @@ public CauseOfBlockage canTake(Node node, Task task) { if (tjp!=null && tjp.getThrottleEnabled()) { CauseOfBlockage cause = canRun(task, tjp); if (cause != null) return cause; - if (tjp.getThrottleOption().equals("project")) { if (tjp.getMaxConcurrentPerNode().intValue() > 0) { int maxConcurrentPerNode = tjp.getMaxConcurrentPerNode().intValue(); @@ -90,6 +93,10 @@ else if (tjp.getThrottleOption().equals("category")) { public CauseOfBlockage canRun(Queue.Item item) { ThrottleJobProperty tjp = getThrottleJobProperty(item.task); if (tjp!=null && tjp.getThrottleEnabled()) { + if (tjp.getlimitOneJobWithMatchingParams() && isAnotherBuildWithSameParametersRunningOnAnyNode(item)) { + LOGGER.info("A build with matching parameters is already running."); + return CauseOfBlockage.fromMessage(Messages._ThrottleQueueTaskDispatcher_OnlyOneWithMatchingParameters()); + } return canRun(item.task, tjp); } return null; @@ -151,6 +158,62 @@ else if (tjp.getThrottleOption().equals("category")) { } + private boolean isAnotherBuildWithSameParametersRunningOnAnyNode(Queue.Item item) { + if (isAnotherBuildWithSameParametersRunningOnNode(Hudson.getInstance(), item)) { + return true; + } + + for (Node node : Hudson.getInstance().getNodes()) { + if (isAnotherBuildWithSameParametersRunningOnNode(node, item)) { + return true; + } + } + return false; + } + + private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.Item item) { + List itemParams = getParametersFromQueueItem(item); + Computer computer = node.toComputer(); + + if (computer != null) { + for (Executor exec : computer.getExecutors()) { + if (exec.getCurrentExecutable() != null && exec.getCurrentExecutable().getParent() == item.task) { + List executingUnitParams = getParametersFromWorkUnit(exec.getCurrentWorkUnit()); + + if (itemParams.equals(executingUnitParams)) { + LOGGER.info("A build with identical parameters is already running."); + return true; + } + } + } + } + return false; + } + + public List getParametersFromWorkUnit(WorkUnit unit) { + List paramsList = new ArrayList(); + + List actions = unit.context.actions; + for (Action action : actions) { + if (action instanceof ParametersAction) { + ParametersAction params = (ParametersAction) action; + paramsList = params.getParameters(); + } + } + return paramsList; + } + + public List getParametersFromQueueItem(Queue.Item item) { + List paramsList = new ArrayList(); + + ParametersAction params = item.getAction(ParametersAction.class); + if (params != null) { + paramsList = params.getParameters(); + } + return paramsList; + } + + private ThrottleJobProperty getThrottleJobProperty(Task task) { if (task instanceof AbstractProject) { AbstractProject p = (AbstractProject) task; @@ -224,4 +287,4 @@ private List> getCategoryProjects(String category) { private static final Logger LOGGER = Logger.getLogger(ThrottleQueueTaskDispatcher.class.getName()); -} +} \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/throttleconcurrents/Messages.properties b/src/main/resources/hudson/plugins/throttleconcurrents/Messages.properties index bb5815d4..94bf9bdd 100644 --- a/src/main/resources/hudson/plugins/throttleconcurrents/Messages.properties +++ b/src/main/resources/hudson/plugins/throttleconcurrents/Messages.properties @@ -1,3 +1,4 @@ ThrottleQueueTaskDispatcher.MaxCapacityOnNode=Already running {0} builds on node ThrottleQueueTaskDispatcher.MaxCapacityTotal=Already running {0} builds across all nodes -ThrottleQueueTaskDispatcher.BuildPending=A build is pending launch \ No newline at end of file +ThrottleQueueTaskDispatcher.BuildPending=A build is pending launch +ThrottleQueueTaskDispatcher.OnlyOneWithMatchingParameters=A build with matching parameters is already running diff --git a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly index a805a6d2..bf60dfd1 100644 --- a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly +++ b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly @@ -20,6 +20,11 @@ field="maxConcurrentPerNode"> + + + diff --git a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/help-limitOneJobWithMatchingParams.html b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/help-limitOneJobWithMatchingParams.html new file mode 100644 index 00000000..f4020d0d --- /dev/null +++ b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/help-limitOneJobWithMatchingParams.html @@ -0,0 +1,4 @@ +
+

If this box is checked, only one instance of the job with matching parameters will be allowed to run at a given time. + Other instances of this job with different parameters will be allowed to run concurrently.

+
From 75a1598f010d5ddbb46a4467fe1234746a19d2b1 Mon Sep 17 00:00:00 2001 From: joe miller Date: Mon, 25 Feb 2013 16:48:11 -0800 Subject: [PATCH 02/22] allow limiting job concurrency against a list of parameters --- .../ThrottleJobProperty.java | 10 +++++- .../ThrottleQueueTaskDispatcher.java | 35 +++++++++++++++++-- .../ThrottleJobProperty/config.jelly | 17 ++++++--- .../help-limitOneJobWithMatchingParams.html | 2 ++ 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java index 6d21de77..47ebac9d 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java @@ -35,6 +35,7 @@ public class ThrottleJobProperty extends JobProperty> { private boolean throttleEnabled; private String throttleOption; private boolean limitOneJobWithMatchingParams; + private String limitOneJobByParams; /** * Store a config version so we're able to migrate config on various @@ -48,13 +49,15 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode, List categories, boolean throttleEnabled, String throttleOption, - boolean limitOneJobWithMatchingParams) { + boolean limitOneJobWithMatchingParams, + String limitOneJobByParams) { this.maxConcurrentPerNode = maxConcurrentPerNode == null ? 0 : maxConcurrentPerNode; this.maxConcurrentTotal = maxConcurrentTotal == null ? 0 : maxConcurrentTotal; this.categories = categories; this.throttleEnabled = throttleEnabled; this.throttleOption = throttleOption; this.limitOneJobWithMatchingParams = limitOneJobWithMatchingParams; + this.limitOneJobByParams = limitOneJobByParams; } @@ -83,6 +86,7 @@ public Object readResolve() { maxConcurrentTotal = 0; } } + configVersion = 1L; return this; @@ -96,6 +100,10 @@ public boolean getlimitOneJobWithMatchingParams() { return limitOneJobWithMatchingParams; } + public String getLimitOneJobByParams() { + return limitOneJobByParams; + } + public String getThrottleOption() { return throttleOption; } diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 6f59b06a..318520b9 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -18,6 +18,7 @@ import hudson.model.Action; import hudson.model.ParametersAction; +import java.util.Arrays; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -172,16 +173,26 @@ private boolean isAnotherBuildWithSameParametersRunningOnAnyNode(Queue.Item item } private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.Item item) { - List itemParams = getParametersFromQueueItem(item); + ThrottleJobProperty tjp = getThrottleJobProperty(item.task); Computer computer = node.toComputer(); + List paramsToCompare = new ArrayList(); + List itemParams = getParametersFromQueueItem(item); + + if (tjp.getLimitOneJobByParams().length() > 0) { + paramsToCompare = Arrays.asList(tjp.getLimitOneJobByParams().split(",")); + itemParams = doFilterParams(paramsToCompare, itemParams); + } + LOGGER.fine("Checking parameters: " + itemParams + " of new queue item: " + item + " against running executors."); if (computer != null) { for (Executor exec : computer.getExecutors()) { if (exec.getCurrentExecutable() != null && exec.getCurrentExecutable().getParent() == item.task) { List executingUnitParams = getParametersFromWorkUnit(exec.getCurrentWorkUnit()); + executingUnitParams = doFilterParams(paramsToCompare, executingUnitParams); - if (itemParams.equals(executingUnitParams)) { - LOGGER.info("A build with identical parameters is already running."); + if (executingUnitParams.containsAll(itemParams)) { + LOGGER.info("build (" + exec.getCurrentWorkUnit() + ") with identical parameters (" + + executingUnitParams + ") is already running."); return true; } } @@ -190,6 +201,24 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I return false; } + // takes a String array containing a list of params, a List of ParameterValue objects + // and returns a new List with only the desired params in the list. + private List doFilterParams(List params, List OriginalParams) { + if (params.isEmpty()) { + return OriginalParams; + } + + List newParams = new ArrayList(); + + for (ParameterValue p : OriginalParams) { + if (params.contains(p.getName())) { + newParams.add(p); + } + } + LOGGER.fine("returning filtered list: " + newParams); + return newParams; + } + public List getParametersFromWorkUnit(WorkUnit unit) { List paramsList = new ArrayList(); diff --git a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly index bf60dfd1..fd563ef3 100644 --- a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly +++ b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly @@ -20,11 +20,18 @@ field="maxConcurrentPerNode">
- - - + + + + + + + diff --git a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/help-limitOneJobWithMatchingParams.html b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/help-limitOneJobWithMatchingParams.html index f4020d0d..973eab0c 100644 --- a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/help-limitOneJobWithMatchingParams.html +++ b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/help-limitOneJobWithMatchingParams.html @@ -1,4 +1,6 @@

If this box is checked, only one instance of the job with matching parameters will be allowed to run at a given time. Other instances of this job with different parameters will be allowed to run concurrently.

+

Optionally, provide a comma-separated list of parameters to use when comparing jobs. If blank, all parameters + must match for a job to be limited to one running instance.

From 07d22918a1ee596fd1d224c0262b79b05258226d Mon Sep 17 00:00:00 2001 From: joe miller Date: Mon, 25 Feb 2013 16:57:07 -0800 Subject: [PATCH 03/22] added some quick commands to help other devs work on the plugin --- README | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README b/README index 9b892021..80d92f79 100644 --- a/README +++ b/README @@ -1,3 +1,16 @@ Work in progress on a plugin to dynamically allocate labels in order to allow for throttling the number of concurrent builds of a project allowed to run on a given node at one time. + +Contributing +------------ + +### Run / Debug cycle: + +Execute `mvn hpi:run`. This will compile the plugin and launch a Jenkins instance on http://localhost:8080 + +### Create Package (.hpi): + +Execute `mvn hpi:hpi`. This will create `throttle-concurrent.hpi` in the `target/` directory + +For other mvn targets, see: https://jenkins-ci.org/maven-hpi-plugin/ From 05b8ed7ab3b4acc7897a14d74bf6cd0a2ca626c7 Mon Sep 17 00:00:00 2001 From: joe miller Date: Tue, 18 Jun 2013 18:27:01 -0700 Subject: [PATCH 04/22] fix bug with jenkins >1.510 causing stackoverflow. bump version --- pom.xml | 4 ++-- .../throttleconcurrents/ThrottleQueueTaskDispatcher.java | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index a8c810c4..e8d316d5 100644 --- a/pom.xml +++ b/pom.xml @@ -26,13 +26,13 @@ THE SOFTWARE. org.jenkins-ci.plugins plugin - 1.500 + 1.519 throttle-concurrents hpi Jenkins Throttle Concurrent Builds Plug-in - 1.7.3-SNAPSHOT + 1.7.4-SNAPSHOT http://wiki.jenkins-ci.org/display/JENKINS/Throttle+Concurrent+Builds+Plugin Plugin to throttle the number of concurrent builds of a single job per node. diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 318520b9..9ba4efea 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -182,7 +182,6 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I paramsToCompare = Arrays.asList(tjp.getLimitOneJobByParams().split(",")); itemParams = doFilterParams(paramsToCompare, itemParams); } - LOGGER.fine("Checking parameters: " + itemParams + " of new queue item: " + item + " against running executors."); if (computer != null) { for (Executor exec : computer.getExecutors()) { @@ -215,7 +214,6 @@ private List doFilterParams(List params, List Date: Tue, 20 Aug 2013 10:06:02 -0700 Subject: [PATCH 05/22] fix bug where parameter-based job concurrency stops working after jenkins config reload. --- pom.xml | 4 ++-- .../throttleconcurrents/ThrottleQueueTaskDispatcher.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index e8d316d5..f18ae0e9 100644 --- a/pom.xml +++ b/pom.xml @@ -26,13 +26,13 @@ THE SOFTWARE. org.jenkins-ci.plugins plugin - 1.519 + 1.518 throttle-concurrents hpi Jenkins Throttle Concurrent Builds Plug-in - 1.7.4-SNAPSHOT + 1.7.5-SNAPSHOT http://wiki.jenkins-ci.org/display/JENKINS/Throttle+Concurrent+Builds+Plugin Plugin to throttle the number of concurrent builds of a single job per node. diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 9ba4efea..d6be13bf 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -185,7 +185,7 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I if (computer != null) { for (Executor exec : computer.getExecutors()) { - if (exec.getCurrentExecutable() != null && exec.getCurrentExecutable().getParent() == item.task) { + if (exec.getCurrentExecutable() != null && exec.getCurrentExecutable().getParent().getOwnerTask().getName() == item.task.getName()) { List executingUnitParams = getParametersFromWorkUnit(exec.getCurrentWorkUnit()); executingUnitParams = doFilterParams(paramsToCompare, executingUnitParams); From d8d2017dae04f52e3679f16e46ff1a7f15ba8c64 Mon Sep 17 00:00:00 2001 From: Ray Thompson Date: Thu, 20 Feb 2014 14:39:46 -0800 Subject: [PATCH 06/22] Successful build against 1.509.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f18ae0e9..9ffc297f 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ THE SOFTWARE. org.jenkins-ci.plugins plugin - 1.518 + 1.509.4 throttle-concurrents From f4c0ce21555f0ff27bba787b25312abc7fdc3c0a Mon Sep 17 00:00:00 2001 From: Ray Thompson Date: Thu, 20 Feb 2014 14:50:15 -0800 Subject: [PATCH 07/22] Fixing NPE --- .../throttleconcurrents/ThrottleQueueTaskDispatcher.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index db7b7bba..5b9548b9 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -222,7 +222,9 @@ public List getParametersFromWorkUnit(WorkUnit unit) { for (Action action : actions) { if (action instanceof ParametersAction) { ParametersAction params = (ParametersAction) action; - paramsList = params.getParameters(); + if (params != null) { + paramsList = params.getParameters(); + } } } return paramsList; From 805556ef54d1cf30be44ede161fa020bf3942967 Mon Sep 17 00:00:00 2001 From: Ray Thompson Date: Thu, 20 Feb 2014 17:40:51 -0800 Subject: [PATCH 08/22] updating tests so they pass --- .../throttleconcurrents/ThrottleJobPropertyTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java index 4551ce96..9c346d65 100644 --- a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java +++ b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java @@ -21,11 +21,11 @@ public void testGetCategoryProjects() throws Exception { String alpha = "alpha", beta = "beta", gamma = "gamma"; // category names FreeStyleProject p1 = createFreeStyleProject("p1"); FreeStyleProject p2 = createFreeStyleProject("p2"); - p2.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha), false, THROTTLE_OPTION_CATEGORY)); + p2.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha), false, THROTTLE_OPTION_CATEGORY, false, "")); FreeStyleProject p3 = createFreeStyleProject("p3"); - p3.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha, beta), true, THROTTLE_OPTION_CATEGORY)); + p3.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha, beta), true, THROTTLE_OPTION_CATEGORY, false, "")); FreeStyleProject p4 = createFreeStyleProject("p4"); - p4.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(beta, gamma), true, THROTTLE_OPTION_CATEGORY)); + p4.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(beta, gamma), true, THROTTLE_OPTION_CATEGORY, false, "")); // TODO when core dep ≥1.480.3, add cloudbees-folder as a test dependency so we can check jobs inside folders assertProjects(alpha, p3); assertProjects(beta, p3, p4); From 801200eb6bdc7aa4e6b81439ceb643fefda2eae7 Mon Sep 17 00:00:00 2001 From: Kyle Ibrahim Date: Mon, 24 Feb 2014 15:23:35 -0800 Subject: [PATCH 09/22] fix npes --- .../ThrottleQueueTaskDispatcher.java | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 5b9548b9..6da2e09c 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -183,14 +183,19 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I if (computer != null) { for (Executor exec : computer.getExecutors()) { - if (exec.getCurrentExecutable() != null && exec.getCurrentExecutable().getParent().getOwnerTask().getName() == item.task.getName()) { - List executingUnitParams = getParametersFromWorkUnit(exec.getCurrentWorkUnit()); - executingUnitParams = doFilterParams(paramsToCompare, executingUnitParams); - - if (executingUnitParams.containsAll(itemParams)) { - LOGGER.info("build (" + exec.getCurrentWorkUnit() + ") with identical parameters (" + - executingUnitParams + ") is already running."); - return true; + if (item != null && item.task != null) { + if (exec.getCurrentExecutable() != null && + exec.getCurrentExecutable().getParent() != null && + exec.getCurrentExecutable().getParent().getOwnerTask() != null && + exec.getCurrentExecutable().getParent().getOwnerTask().getName() == item.task.getName()) { + List executingUnitParams = getParametersFromWorkUnit(exec.getCurrentWorkUnit()); + executingUnitParams = doFilterParams(paramsToCompare, executingUnitParams); + + if (executingUnitParams.containsAll(itemParams)) { + LOGGER.info("build (" + exec.getCurrentWorkUnit() + ") with identical parameters (" + + executingUnitParams + ") is already running."); + return true; + } } } } @@ -218,12 +223,14 @@ private List doFilterParams(List params, List getParametersFromWorkUnit(WorkUnit unit) { List paramsList = new ArrayList(); - List actions = unit.context.actions; - for (Action action : actions) { - if (action instanceof ParametersAction) { - ParametersAction params = (ParametersAction) action; - if (params != null) { - paramsList = params.getParameters(); + if (unit != null && unit.context != null && unit.context.actions != null) { + List actions = unit.context.actions; + for (Action action : actions) { + if (action instanceof ParametersAction) { + ParametersAction params = (ParametersAction) action; + if (params != null) { + paramsList = params.getParameters(); + } } } } From 77ad29d446691b15e0134a1d5ed0c0f911939a72 Mon Sep 17 00:00:00 2001 From: Kyle Ibrahim Date: Mon, 24 Feb 2014 15:36:53 -0800 Subject: [PATCH 10/22] helper function --- .../plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 6da2e09c..370e83c2 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -184,6 +184,7 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I if (computer != null) { for (Executor exec : computer.getExecutors()) { if (item != null && item.task != null) { + // TODO: refactor into a nameEquals helper method if (exec.getCurrentExecutable() != null && exec.getCurrentExecutable().getParent() != null && exec.getCurrentExecutable().getParent().getOwnerTask() != null && From 7520a2cc54af14be30d60940136b68b9c34d5999 Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Wed, 19 Mar 2014 12:54:24 -0400 Subject: [PATCH 11/22] replace deprecated method --- .../throttleconcurrents/ThrottleQueueTaskDispatcher.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 370e83c2..e7cbfca0 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -30,7 +30,8 @@ public class ThrottleQueueTaskDispatcher extends QueueTaskDispatcher { @Override - public CauseOfBlockage canTake(Node node, Task task) { + public CauseOfBlockage canTake(Node node, Queue.BuildableItem item) { + Task task = item.task; if (task instanceof MatrixConfiguration) { return null; } @@ -188,7 +189,7 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I if (exec.getCurrentExecutable() != null && exec.getCurrentExecutable().getParent() != null && exec.getCurrentExecutable().getParent().getOwnerTask() != null && - exec.getCurrentExecutable().getParent().getOwnerTask().getName() == item.task.getName()) { + exec.getCurrentExecutable().getParent().getOwnerTask().getName().equals(item.task.getName())) { List executingUnitParams = getParametersFromWorkUnit(exec.getCurrentWorkUnit()); executingUnitParams = doFilterParams(paramsToCompare, executingUnitParams); From 12b7aacdbc49ffd838649a033ddaf83a93724c31 Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Wed, 19 Mar 2014 12:54:46 -0400 Subject: [PATCH 12/22] fix test by changing select options to upper case --- .../throttleconcurrents/ThrottleQueueTaskDispatcherTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcherTest.java b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcherTest.java index 529a1870..ecc94851 100644 --- a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcherTest.java +++ b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcherTest.java @@ -358,7 +358,7 @@ private String configureLogger() input.setValueAttribute(logger); } HtmlSelect select = form.getSelectByName("level"); - HtmlOption option = select.getOptionByValue("fine"); + HtmlOption option = select.getOptionByValue("FINE"); select.setSelectedAttribute(option, true); break; } From 1a32b7d4f7b17a7e869e6c08b075c0060f518310 Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Wed, 19 Mar 2014 13:26:26 -0400 Subject: [PATCH 13/22] add .idea directory to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 77fb9a7d..c9c57ab6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.iml *.ipr *.iws +.idea target work # eclipse => From cc0da5ab8d9013411a992bda9d1e58fd2065769d Mon Sep 17 00:00:00 2001 From: Zihao Yu Date: Thu, 20 Mar 2014 07:55:02 -0400 Subject: [PATCH 14/22] fix comment typo --- .../throttleconcurrents/ThrottleQueueTaskDispatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index e7cbfca0..d3abd654 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -90,7 +90,7 @@ else if (tjp.getThrottleOption().equals("category")) { return null; } - // @Override on jenkins 4.127+ , but still compatible with 1.399 + // @Override on jenkins 1.427+ , but still compatible with 1.399 public CauseOfBlockage canRun(Queue.Item item) { ThrottleJobProperty tjp = getThrottleJobProperty(item.task); if (tjp!=null && tjp.getThrottleEnabled()) { From d200a5797fdd37027ba2b10cb8df0005d56bf7bf Mon Sep 17 00:00:00 2001 From: jmozmoz Date: Sun, 25 Oct 2015 00:12:52 +0200 Subject: [PATCH 15/22] Implement (most) review requests in https://github.com/jenkinsci/throttle-concurrent-builds-plugin/pull/9 --- .../ThrottleJobProperty.java | 45 ++++++++++--------- .../ThrottleQueueTaskDispatcher.java | 14 +++--- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java index e68a7aef..bfe7109f 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java @@ -31,7 +31,7 @@ public class ThrottleJobProperty extends JobProperty> { // Moving category to categories, to support, well, multiple categories per job. @Deprecated transient String category; - + private Integer maxConcurrentPerNode; private Integer maxConcurrentTotal; private List categories; @@ -47,7 +47,7 @@ public class ThrottleJobProperty extends JobProperty> { * functionality upgrades. */ private Long configVersion; - + @DataBoundConstructor public ThrottleJobProperty(Integer maxConcurrentPerNode, Integer maxConcurrentTotal, @@ -96,7 +96,7 @@ public Object readResolve() { } configVersion = 1L; - + return this; } @@ -121,10 +121,11 @@ public boolean getThrottleEnabled() { return throttleEnabled; } - public boolean getlimitOneJobWithMatchingParams() { + public boolean isLimitOneJobWithMatchingParams() { return limitOneJobWithMatchingParams; } + public String getLimitOneJobByParams() { return limitOneJobByParams; } @@ -132,22 +133,22 @@ public String getLimitOneJobByParams() { public String getThrottleOption() { return throttleOption; } - + public List getCategories() { return categories; } - + public Integer getMaxConcurrentPerNode() { if (maxConcurrentPerNode == null) maxConcurrentPerNode = 0; - + return maxConcurrentPerNode; } public Integer getMaxConcurrentTotal() { if (maxConcurrentTotal == null) maxConcurrentTotal = 0; - + return maxConcurrentTotal; } @@ -179,24 +180,24 @@ private static Item getItem(ItemGroup group, String name) { return group.getItem(name); } } - + @Extension public static final class DescriptorImpl extends JobPropertyDescriptor { private List categories; - + /** Map from category names, to properties including that category. */ private Map> propertiesByCategory = new HashMap>(); - + public DescriptorImpl() { super(ThrottleJobProperty.class); load(); } - + @Override public String getDisplayName() { return "Throttle Concurrent Builds"; } - + @Override @SuppressWarnings("rawtypes") public boolean isApplicable(Class jobType) { @@ -236,10 +237,10 @@ public FormValidation doCheckMaxConcurrentTotal(@QueryParameter String value) { return checkNullOrInt(value); } - + public ThrottleCategory getCategoryByName(String categoryName) { ThrottleCategory category = null; - + for (ThrottleCategory tc : categories) { if (tc.getCategoryName().equals(categoryName)) { category = tc; @@ -252,7 +253,7 @@ public ThrottleCategory getCategoryByName(String categoryName) { public void setCategories(List categories) { this.categories = categories; } - + public List getCategories() { if (categories == null) { categories = new ArrayList(); @@ -265,14 +266,14 @@ public ListBoxModel doFillCategoryItems() { ListBoxModel m = new ListBoxModel(); m.add("(none)", ""); - + for (ThrottleCategory tc : getCategories()) { m.add(tc.getCategoryName()); } return m; } - + } public static final class ThrottleCategory extends AbstractDescribableImpl { @@ -292,18 +293,18 @@ public ThrottleCategory(String categoryName, this.nodeLabeledPairs = nodeLabeledPairs == null ? new ArrayList() : nodeLabeledPairs; } - + public Integer getMaxConcurrentPerNode() { if (maxConcurrentPerNode == null) maxConcurrentPerNode = 0; - + return maxConcurrentPerNode; } - + public Integer getMaxConcurrentTotal() { if (maxConcurrentTotal == null) maxConcurrentTotal = 0; - + return maxConcurrentTotal; } diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index c666418f..e6a499a6 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -94,7 +94,7 @@ else if (tjp.getThrottleOption().equals("category")) { public CauseOfBlockage canRun(Queue.Item item) { ThrottleJobProperty tjp = getThrottleJobProperty(item.task); if (tjp!=null && tjp.getThrottleEnabled()) { - if (tjp.getlimitOneJobWithMatchingParams() && isAnotherBuildWithSameParametersRunningOnAnyNode(item)) { + if (tjp.isLimitOneJobWithMatchingParams() && isAnotherBuildWithSameParametersRunningOnAnyNode(item)) { LOGGER.info("A build with matching parameters is already running."); return CauseOfBlockage.fromMessage(Messages._ThrottleQueueTaskDispatcher_OnlyOneWithMatchingParameters()); } @@ -189,13 +189,13 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I if (exec.getCurrentExecutable() != null && exec.getCurrentExecutable().getParent() != null && exec.getCurrentExecutable().getParent().getOwnerTask() != null && - exec.getCurrentExecutable().getParent().getOwnerTask().getName().equals(item.task.getName())) { + exec.getCurrentExecutable().getParent().getOwnerTask().getName().equals(item.task.getDisplayName())) { List executingUnitParams = getParametersFromWorkUnit(exec.getCurrentWorkUnit()); executingUnitParams = doFilterParams(paramsToCompare, executingUnitParams); if (executingUnitParams.containsAll(itemParams)) { - LOGGER.info("build (" + exec.getCurrentWorkUnit() + ") with identical parameters (" + - executingUnitParams + ") is already running."); + LOGGER.debug("build (" + exec.getCurrentWorkUnit() + ") with identical parameters (" + + executingUnitParams + ") is already running."); return true; } } @@ -240,12 +240,16 @@ public List getParametersFromWorkUnit(WorkUnit unit) { } public List getParametersFromQueueItem(Queue.Item item) { - List paramsList = new ArrayList(); + List paramsList; ParametersAction params = item.getAction(ParametersAction.class); if (params != null) { paramsList = params.getParameters(); } + else + { + paramsList = new ArrayList(); + } return paramsList; } From 599453b44ee6423a2827be9d540f3ef48f019774 Mon Sep 17 00:00:00 2001 From: jmozmoz Date: Fri, 8 Jan 2016 14:55:46 +0100 Subject: [PATCH 16/22] Store parameters to compare in a transient list build on save/load --- .../ThrottleJobProperty.java | 24 +++++++++++++++++++ .../ThrottleQueueTaskDispatcher.java | 3 +-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java index bfe7109f..25693acb 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java @@ -42,6 +42,8 @@ public class ThrottleJobProperty extends JobProperty> { private transient boolean throttleConfiguration; private @CheckForNull ThrottleMatrixProjectOptions matrixOptions; + private transient List paramsToCompare; + /** * Store a config version so we're able to migrate config on various * functionality upgrades. @@ -66,6 +68,7 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode, this.limitOneJobWithMatchingParams = limitOneJobWithMatchingParams; this.limitOneJobByParams = limitOneJobByParams; this.matrixOptions = matrixOptions; + this.paramToCompare = new ArrayList(); } @@ -173,6 +176,11 @@ static List> getCategoryProjects(String category) { } return categoryProjects; } + + public List getParamsToCompare() { + return paramsToCompare; + } + private static Item getItem(ItemGroup group, String name) { if (group instanceof Jenkins) { return ((Jenkins) group).getItemMap().get(name); @@ -211,6 +219,22 @@ public boolean configure(StaplerRequest req, JSONObject formData) throws FormExc return true; } + @Override + public protected void load() { + super.load(); + if (limitOneJobByParams.length() > 0) { + paramsToCompare = Arrays.asList(limitOneJobByParams.split(",")); + } + } + + @Override + public protected void save() { + super.save(); + if (limitOneJobByParams.length() > 0) { + paramsToCompare = Arrays.asList(limitOneJobByParams.split(",")); + } + } + public FormValidation doCheckCategoryName(@QueryParameter String value) { if (Util.fixEmptyAndTrim(value) == null) { return FormValidation.error("Empty category names are not allowed."); diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index e6a499a6..4c9a329d 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -174,11 +174,10 @@ private boolean isAnotherBuildWithSameParametersRunningOnAnyNode(Queue.Item item private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.Item item) { ThrottleJobProperty tjp = getThrottleJobProperty(item.task); Computer computer = node.toComputer(); - List paramsToCompare = new ArrayList(); + List paramsToCompare = tjp.getParamsToCompare(); List itemParams = getParametersFromQueueItem(item); if (tjp.getLimitOneJobByParams().length() > 0) { - paramsToCompare = Arrays.asList(tjp.getLimitOneJobByParams().split(",")); itemParams = doFilterParams(paramsToCompare, itemParams); } From e96ca2b7df6122c6866de649acefa3481f95c601 Mon Sep 17 00:00:00 2001 From: jmozmoz Date: Sun, 10 Jan 2016 01:21:01 +0100 Subject: [PATCH 17/22] Changes requested by reviewer: - Rename limitOneJobByParams to paramsToUseForLimit - Change style of method header comment --- .../ThrottleJobProperty.java | 19 +++++++------------ .../ThrottleQueueTaskDispatcher.java | 10 +++++++--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java index 25693acb..34397f67 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java @@ -38,7 +38,7 @@ public class ThrottleJobProperty extends JobProperty> { private boolean throttleEnabled; private String throttleOption; private boolean limitOneJobWithMatchingParams; - private String limitOneJobByParams; + private String paramsToUseForLimit; private transient boolean throttleConfiguration; private @CheckForNull ThrottleMatrixProjectOptions matrixOptions; @@ -57,7 +57,7 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode, boolean throttleEnabled, String throttleOption, boolean limitOneJobWithMatchingParams, - String limitOneJobByParams, + String paramsToUseForLimit, @CheckForNull ThrottleMatrixProjectOptions matrixOptions ) { this.maxConcurrentPerNode = maxConcurrentPerNode == null ? 0 : maxConcurrentPerNode; @@ -66,7 +66,7 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode, this.throttleEnabled = throttleEnabled; this.throttleOption = throttleOption; this.limitOneJobWithMatchingParams = limitOneJobWithMatchingParams; - this.limitOneJobByParams = limitOneJobByParams; + this.paramsToUseForLimit = paramsToUseForLimit; this.matrixOptions = matrixOptions; this.paramToCompare = new ArrayList(); } @@ -128,11 +128,6 @@ public boolean isLimitOneJobWithMatchingParams() { return limitOneJobWithMatchingParams; } - - public String getLimitOneJobByParams() { - return limitOneJobByParams; - } - public String getThrottleOption() { return throttleOption; } @@ -222,16 +217,16 @@ public boolean configure(StaplerRequest req, JSONObject formData) throws FormExc @Override public protected void load() { super.load(); - if (limitOneJobByParams.length() > 0) { - paramsToCompare = Arrays.asList(limitOneJobByParams.split(",")); + if (paramsToUseForLimit.length() > 0) { + paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); } } @Override public protected void save() { super.save(); - if (limitOneJobByParams.length() > 0) { - paramsToCompare = Arrays.asList(limitOneJobByParams.split(",")); + if (paramsToUseForLimit.length() > 0) { + paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); } } diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 4c9a329d..83226df3 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -177,7 +177,7 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I List paramsToCompare = tjp.getParamsToCompare(); List itemParams = getParametersFromQueueItem(item); - if (tjp.getLimitOneJobByParams().length() > 0) { + if (paramsToCompare.length() > 0) { itemParams = doFilterParams(paramsToCompare, itemParams); } @@ -204,8 +204,12 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I return false; } - // takes a String array containing a list of params, a List of ParameterValue objects - // and returns a new List with only the desired params in the list. + /** + * Filter job parameters to only include parameters used for throttling + * @param String array containing a list of params to be used for throttling. + * @param List of ParameterValue objects of the job. + * @return List with only the desired params in the list. + */ private List doFilterParams(List params, List OriginalParams) { if (params.isEmpty()) { return OriginalParams; From 5d717537c38955b97f66ede795aafd76b6fa8b27 Mon Sep 17 00:00:00 2001 From: jmozmoz Date: Sun, 10 Jan 2016 01:59:37 +0100 Subject: [PATCH 18/22] Removed obsolet logging. --- .../plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 513af58f..b2417868 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -96,7 +96,6 @@ public CauseOfBlockage canRun(Queue.Item item) { ThrottleJobProperty tjp = getThrottleJobProperty(item.task); if (tjp!=null && tjp.getThrottleEnabled()) { if (tjp.isLimitOneJobWithMatchingParams() && isAnotherBuildWithSameParametersRunningOnAnyNode(item)) { - LOGGER.info("A build with matching parameters is already running."); return CauseOfBlockage.fromMessage(Messages._ThrottleQueueTaskDispatcher_OnlyOneWithMatchingParameters()); } return canRun(item.task, tjp); From c5b5090326d6bc1aa1dfa365c4aca7d672c5a7ee Mon Sep 17 00:00:00 2001 From: jmozmoz Date: Sun, 10 Jan 2016 15:58:02 +0100 Subject: [PATCH 19/22] Remove compilation error. FIXME: paramsToUseForLimit not loaded into configuration form --- .../ThrottleJobProperty.java | 118 +++++++++++++----- .../ThrottleQueueTaskDispatcher.java | 17 ++- .../ThrottleJobProperty/config.jelly | 2 +- 3 files changed, 101 insertions(+), 36 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java index d9769311..d4cbadee 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java @@ -13,6 +13,7 @@ import hudson.util.ListBoxModel; import hudson.Util; +import java.util.Arrays; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -24,6 +25,9 @@ import javax.annotation.CheckForNull; import jenkins.model.Jenkins; +import java.util.logging.Level; +import java.util.logging.Logger; + import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; @@ -40,10 +44,10 @@ public class ThrottleJobProperty extends JobProperty> { private boolean throttleEnabled; private String throttleOption; private boolean limitOneJobWithMatchingParams; - private String paramsToUseForLimit; private transient boolean throttleConfiguration; private @CheckForNull ThrottleMatrixProjectOptions matrixOptions; + private String paramsToUseForLimit; private transient List paramsToCompare; /** @@ -68,11 +72,24 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode, this.throttleEnabled = throttleEnabled; this.throttleOption = throttleOption; this.limitOneJobWithMatchingParams = limitOneJobWithMatchingParams; - this.paramsToUseForLimit = paramsToUseForLimit; this.matrixOptions = matrixOptions; - this.paramToCompare = new ArrayList(); - } + this.paramsToUseForLimit = paramsToUseForLimit; + LOGGER.log(Level.INFO, "ThrottleJobProperty"); + LOGGER.log(Level.INFO, paramsToUseForLimit); + + if ((this.paramsToUseForLimit != null)) { + LOGGER.log(Level.INFO, "paramsToUseForLimit != null"); + if ((this.paramsToUseForLimit.length() > 0)) { + LOGGER.log(Level.INFO, "paramsToUseForLimit.length() > 0"); + this.paramsToCompare = Arrays.asList(this.paramsToUseForLimit.split(",")); + } + } + else { + LOGGER.log(Level.INFO, "paramsToUseForLimit == null"); + this.paramsToCompare = new ArrayList(); + } + } /** * Migrates deprecated/obsolete data @@ -152,6 +169,23 @@ public Integer getMaxConcurrentTotal() { return maxConcurrentTotal; } + public List getParamsToCompare() { + if (paramsToCompare == null) { + if ((paramsToUseForLimit != null)) { + LOGGER.log(Level.INFO, "paramsToUseForLimit != null"); + if ((paramsToUseForLimit.length() > 0)) { + LOGGER.log(Level.INFO, "paramsToUseForLimit.length() > 0"); + paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); + } + } + else { + LOGGER.log(Level.INFO, "paramsToUseForLimit == null"); + paramsToCompare = new ArrayList(); + } + } + return paramsToCompare; + } + static List getCategoryTasks(String category) { assert category != null && !category.equals(""); List categoryTasks = new ArrayList(); @@ -175,10 +209,6 @@ static List getCategoryTasks(String category) { return categoryTasks; } - public List getParamsToCompare() { - return paramsToCompare; - } - private static Item getItem(ItemGroup group, String name) { if (group instanceof Jenkins) { return ((Jenkins) group).getItemMap().get(name); @@ -190,12 +220,16 @@ private static Item getItem(ItemGroup group, String name) { @Extension public static final class DescriptorImpl extends JobPropertyDescriptor { private List categories; +// private transient List paramsToCompare; +// private String paramsToUseForLimit; + /** Map from category names, to properties including that category. */ private Map> propertiesByCategory = new HashMap>(); public DescriptorImpl() { super(ThrottleJobProperty.class); + LOGGER.log(Level.INFO, "DescriptorImpl(): "); load(); } @@ -210,28 +244,46 @@ public boolean isApplicable(Class jobType) { return Job.class.isAssignableFrom(jobType) && Queue.Task.class.isAssignableFrom(jobType); } - @Override - public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { - req.bindJSON(this, formData); - save(); - return true; - } - - @Override - public protected void load() { - super.load(); - if (paramsToUseForLimit.length() > 0) { - paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); - } - } - - @Override - public protected void save() { - super.save(); - if (paramsToUseForLimit.length() > 0) { - paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); - } - } +// @Override +// public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { +// req.bindJSON(this, formData); +// save(); +// return true; +// } + +// @Override +// public void load() { +// super.load(); +// LOGGER.log(Level.INFO, "load(): "); +// if ((paramsToUseForLimit != null)) { +// LOGGER.log(Level.INFO, "paramsToUseForLimit != null"); +// if ((paramsToUseForLimit.length() > 0)) { +// LOGGER.log(Level.INFO, "paramsToUseForLimit.length() > 0"); +// paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); +// } +// } +// else { +// LOGGER.log(Level.INFO, "paramsToUseForLimit == null"); +// paramsToCompare = new ArrayList(); +// } +// } +// +// @Override +// public void save() { +// super.save(); +// LOGGER.log(Level.INFO, "save(): "); +// if ((paramsToUseForLimit != null)) { +// LOGGER.log(Level.INFO, "paramsToUseForLimit != null"); +// if ((paramsToUseForLimit.length() > 0)) { +// LOGGER.log(Level.INFO, "paramsToUseForLimit.length() > 0"); +// paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); +// } +// } +// else { +// LOGGER.log(Level.INFO, "paramsToUseForLimit == null"); +// paramsToCompare = new ArrayList(); +// } +// } public FormValidation doCheckCategoryName(@QueryParameter String value) { if (Util.fixEmptyAndTrim(value) == null) { @@ -272,6 +324,10 @@ public ThrottleCategory getCategoryByName(String categoryName) { return category; } +// public List getParamsToCompare() { +// return paramsToCompare; +// } + public void setCategories(List categories) { this.categories = categories; } @@ -387,4 +443,6 @@ public String getDisplayName() { } } } + + private static final Logger LOGGER = Logger.getLogger(ThrottleQueueTaskDispatcher.class.getName()); } diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index b2417868..42dea06a 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -19,13 +19,13 @@ import hudson.model.Action; import hudson.model.ParametersAction; -import java.util.Arrays; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import javax.annotation.CheckForNull; @Extension public class ThrottleQueueTaskDispatcher extends QueueTaskDispatcher { @@ -177,10 +177,16 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I List paramsToCompare = tjp.getParamsToCompare(); List itemParams = getParametersFromQueueItem(item); - if (paramsToCompare.length() > 0) { + LOGGER.log(Level.INFO, "all params to compare: " + + paramsToCompare); + + if (paramsToCompare.size() > 0) { itemParams = doFilterParams(paramsToCompare, itemParams); } + LOGGER.log(Level.INFO, "queued job params to compare: " + + paramsToCompare); + if (computer != null) { for (Executor exec : computer.getExecutors()) { if (item != null && item.task != null) { @@ -193,8 +199,9 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I executingUnitParams = doFilterParams(paramsToCompare, executingUnitParams); if (executingUnitParams.containsAll(itemParams)) { - LOGGER.debug("build (" + exec.getCurrentWorkUnit() + ") with identical parameters (" + - executingUnitParams + ") is already running."); + LOGGER.log(Level.INFO, "build (" + exec.getCurrentWorkUnit() + + ") with identical parameters (" + + executingUnitParams + ") is already running."); return true; } } @@ -345,5 +352,5 @@ private int getMaxConcurrentPerNodeBasedOnMatchingLabels( return maxConcurrentPerNodeLabeledIfMatch; } - private static final Logger LOGGER = Logger.getLogger(ThrottleQueueTaskDispatcher.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ThrottleJobProperty.class.getName()); } diff --git a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly index 05dbad17..042b5b48 100644 --- a/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly +++ b/src/main/resources/hudson/plugins/throttleconcurrents/ThrottleJobProperty/config.jelly @@ -26,7 +26,7 @@ inline="true" checked="${instance.limitOneJobWithMatchingParams}"> From af343495c528d777c4f4f50c75a088bb492cd2d2 Mon Sep 17 00:00:00 2001 From: jmozmoz Date: Sun, 10 Jan 2016 16:38:53 +0100 Subject: [PATCH 20/22] Remove obsolete code. Add getter for paramsToUseForLimit --- .../ThrottleJobProperty.java | 73 ++++--------------- .../ThrottleQueueTaskDispatcher.java | 2 +- 2 files changed, 17 insertions(+), 58 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java index d4cbadee..d959617e 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java @@ -74,18 +74,15 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode, this.limitOneJobWithMatchingParams = limitOneJobWithMatchingParams; this.matrixOptions = matrixOptions; this.paramsToUseForLimit = paramsToUseForLimit; - LOGGER.log(Level.INFO, "ThrottleJobProperty"); - LOGGER.log(Level.INFO, paramsToUseForLimit); - if ((this.paramsToUseForLimit != null)) { - LOGGER.log(Level.INFO, "paramsToUseForLimit != null"); if ((this.paramsToUseForLimit.length() > 0)) { - LOGGER.log(Level.INFO, "paramsToUseForLimit.length() > 0"); this.paramsToCompare = Arrays.asList(this.paramsToUseForLimit.split(",")); } + else { + this.paramsToCompare = new ArrayList(); + } } else { - LOGGER.log(Level.INFO, "paramsToUseForLimit == null"); this.paramsToCompare = new ArrayList(); } @@ -169,17 +166,21 @@ public Integer getMaxConcurrentTotal() { return maxConcurrentTotal; } + public String getParamsToUseForLimit() { + return paramsToUseForLimit; + } + public List getParamsToCompare() { if (paramsToCompare == null) { if ((paramsToUseForLimit != null)) { - LOGGER.log(Level.INFO, "paramsToUseForLimit != null"); if ((paramsToUseForLimit.length() > 0)) { - LOGGER.log(Level.INFO, "paramsToUseForLimit.length() > 0"); paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); } + else { + paramsToCompare = new ArrayList(); + } } else { - LOGGER.log(Level.INFO, "paramsToUseForLimit == null"); paramsToCompare = new ArrayList(); } } @@ -220,9 +221,6 @@ private static Item getItem(ItemGroup group, String name) { @Extension public static final class DescriptorImpl extends JobPropertyDescriptor { private List categories; -// private transient List paramsToCompare; -// private String paramsToUseForLimit; - /** Map from category names, to properties including that category. */ private Map> propertiesByCategory = new HashMap>(); @@ -244,46 +242,12 @@ public boolean isApplicable(Class jobType) { return Job.class.isAssignableFrom(jobType) && Queue.Task.class.isAssignableFrom(jobType); } -// @Override -// public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { -// req.bindJSON(this, formData); -// save(); -// return true; -// } - -// @Override -// public void load() { -// super.load(); -// LOGGER.log(Level.INFO, "load(): "); -// if ((paramsToUseForLimit != null)) { -// LOGGER.log(Level.INFO, "paramsToUseForLimit != null"); -// if ((paramsToUseForLimit.length() > 0)) { -// LOGGER.log(Level.INFO, "paramsToUseForLimit.length() > 0"); -// paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); -// } -// } -// else { -// LOGGER.log(Level.INFO, "paramsToUseForLimit == null"); -// paramsToCompare = new ArrayList(); -// } -// } -// -// @Override -// public void save() { -// super.save(); -// LOGGER.log(Level.INFO, "save(): "); -// if ((paramsToUseForLimit != null)) { -// LOGGER.log(Level.INFO, "paramsToUseForLimit != null"); -// if ((paramsToUseForLimit.length() > 0)) { -// LOGGER.log(Level.INFO, "paramsToUseForLimit.length() > 0"); -// paramsToCompare = Arrays.asList(paramsToUseForLimit.split(",")); -// } -// } -// else { -// LOGGER.log(Level.INFO, "paramsToUseForLimit == null"); -// paramsToCompare = new ArrayList(); -// } -// } + @Override + public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { + req.bindJSON(this, formData); + save(); + return true; + } public FormValidation doCheckCategoryName(@QueryParameter String value) { if (Util.fixEmptyAndTrim(value) == null) { @@ -311,7 +275,6 @@ public FormValidation doCheckMaxConcurrentTotal(@QueryParameter String value) { return checkNullOrInt(value); } - public ThrottleCategory getCategoryByName(String categoryName) { ThrottleCategory category = null; @@ -324,10 +287,6 @@ public ThrottleCategory getCategoryByName(String categoryName) { return category; } -// public List getParamsToCompare() { -// return paramsToCompare; -// } - public void setCategories(List categories) { this.categories = categories; } diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 42dea06a..9d3fa113 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -199,7 +199,7 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I executingUnitParams = doFilterParams(paramsToCompare, executingUnitParams); if (executingUnitParams.containsAll(itemParams)) { - LOGGER.log(Level.INFO, "build (" + exec.getCurrentWorkUnit() + + LOGGER.log(Level.FINE, "build (" + exec.getCurrentWorkUnit() + ") with identical parameters (" + executingUnitParams + ") is already running."); return true; From 0e6c0f1b8ef84ba9d85ac829810869fd67c24212 Mon Sep 17 00:00:00 2001 From: jmozmoz Date: Sun, 10 Jan 2016 17:16:41 +0100 Subject: [PATCH 21/22] Remove obsolete debugging output. --- .../throttleconcurrents/ThrottleQueueTaskDispatcher.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index 9d3fa113..a4be8174 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -177,16 +177,10 @@ private boolean isAnotherBuildWithSameParametersRunningOnNode(Node node, Queue.I List paramsToCompare = tjp.getParamsToCompare(); List itemParams = getParametersFromQueueItem(item); - LOGGER.log(Level.INFO, "all params to compare: " + - paramsToCompare); - if (paramsToCompare.size() > 0) { itemParams = doFilterParams(paramsToCompare, itemParams); } - LOGGER.log(Level.INFO, "queued job params to compare: " + - paramsToCompare); - if (computer != null) { for (Executor exec : computer.getExecutors()) { if (item != null && item.task != null) { From 857f6dec9127ceffe3c6bd2c1b01c0c61a1e9094 Mon Sep 17 00:00:00 2001 From: jmozmoz Date: Mon, 11 Jan 2016 01:16:01 +0100 Subject: [PATCH 22/22] Manual merge with upstream master --- .../ThrottleJobProperty.java | 89 +++++++++++---- .../ThrottleQueueTaskDispatcher.java | 55 +++++++-- .../ThrottleJobPropertyTest.java | 108 +++++++++++++++++- .../ThrottleQueueTaskDispatcherTest.java | 2 +- 4 files changed, 218 insertions(+), 36 deletions(-) diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java index d959617e..b8b27255 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleJobProperty.java @@ -1,6 +1,7 @@ package hudson.plugins.throttleconcurrents; import hudson.Extension; +import hudson.matrix.MatrixConfiguration; import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.model.Item; @@ -12,6 +13,8 @@ import hudson.util.FormValidation; import hudson.util.ListBoxModel; import hudson.Util; +import hudson.matrix.MatrixBuild; +import hudson.matrix.MatrixProject; import java.util.Arrays; import java.util.ArrayList; @@ -25,9 +28,6 @@ import javax.annotation.CheckForNull; import jenkins.model.Jenkins; -import java.util.logging.Level; -import java.util.logging.Logger; - import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; @@ -68,7 +68,9 @@ public ThrottleJobProperty(Integer maxConcurrentPerNode, ) { this.maxConcurrentPerNode = maxConcurrentPerNode == null ? 0 : maxConcurrentPerNode; this.maxConcurrentTotal = maxConcurrentTotal == null ? 0 : maxConcurrentTotal; - this.categories = categories; + this.categories = categories == null ? + new CopyOnWriteArrayList() : + new CopyOnWriteArrayList(categories); this.throttleEnabled = throttleEnabled; this.throttleOption = throttleOption; this.limitOneJobWithMatchingParams = limitOneJobWithMatchingParams; @@ -96,7 +98,7 @@ public Object readResolve() { configVersion = 0L; } if (categories == null) { - categories = new ArrayList(); + categories = new CopyOnWriteArrayList(); } if (category != null) { categories.add(category); @@ -115,20 +117,25 @@ public Object readResolve() { } configVersion = 1L; - + + // Handle the throttleConfiguration in custom builds (not released) + if (throttleConfiguration && matrixOptions == null) { + matrixOptions = new ThrottleMatrixProjectOptions(false, true); + } + return this; } @Override protected void setOwner(Job owner) { super.setOwner(owner); if (throttleEnabled && categories != null) { - Map> propertiesByCategory = ((DescriptorImpl) getDescriptor()).propertiesByCategory; - synchronized (propertiesByCategory) { + DescriptorImpl descriptor = (DescriptorImpl) getDescriptor(); + synchronized (descriptor.propertiesByCategoryLock) { for (String c : categories) { - Map properties = propertiesByCategory.get(c); + Map properties = descriptor.propertiesByCategory.get(c); if (properties == null) { properties = new WeakHashMap(); - propertiesByCategory.put(c, properties); + descriptor.propertiesByCategory.put(c, properties); } properties.put(this, null); } @@ -170,6 +177,29 @@ public String getParamsToUseForLimit() { return paramsToUseForLimit; } + @CheckForNull + public ThrottleMatrixProjectOptions getMatrixOptions() { + return matrixOptions; + } + + /** + * Check if the build throttles {@link MatrixBuild}s. + */ + public boolean isThrottleMatrixBuilds() { + return matrixOptions != null + ? matrixOptions.isThrottleMatrixBuilds() + : ThrottleMatrixProjectOptions.DEFAULT.isThrottleMatrixBuilds(); + } + + /** + * Check if the build throttles {@link MatrixConfiguration}s. + */ + public boolean isThrottleMatrixConfigurations() { + return matrixOptions != null + ? matrixOptions.isThrottleMatrixConfigurations() + : ThrottleMatrixProjectOptions.DEFAULT.isThrottleMatrixConfigurations(); + } + public List getParamsToCompare() { if (paramsToCompare == null) { if ((paramsToUseForLimit != null)) { @@ -191,9 +221,9 @@ static List getCategoryTasks(String category) { assert category != null && !category.equals(""); List categoryTasks = new ArrayList(); Collection properties; - Map> propertiesByCategory = Jenkins.getInstance().getDescriptorByType(DescriptorImpl.class).propertiesByCategory; - synchronized (propertiesByCategory) { - Map _properties = propertiesByCategory.get(category); + DescriptorImpl descriptor = Jenkins.getInstance().getDescriptorByType(DescriptorImpl.class); + synchronized (descriptor.propertiesByCategoryLock) { + Map _properties = descriptor.propertiesByCategory.get(category); properties = _properties != null ? new ArrayList(_properties.keySet()) : Collections.emptySet(); } for (ThrottleJobProperty t : properties) { @@ -203,6 +233,11 @@ static List getCategoryTasks(String category) { if (/*is a task*/ p instanceof Queue.Task && /* not deleted */getItem(p.getParent(), p.getName()) == p && /* has not since been reconfigured */ p.getProperty(ThrottleJobProperty.class) == t) { categoryTasks.add((Queue.Task) p); + if (p instanceof MatrixProject && t.isThrottleMatrixConfigurations()) { + for (MatrixConfiguration mc : ((MatrixProject)p).getActiveConfigurations()) { + categoryTasks.add(mc); + } + } } } } @@ -223,12 +258,24 @@ public static final class DescriptorImpl extends JobPropertyDescriptor { private List categories; /** Map from category names, to properties including that category. */ - private Map> propertiesByCategory = new HashMap>(); + private transient Map> propertiesByCategory + = new HashMap>(); + /** A sync object for {@link #propertiesByCategory} */ + private final transient Object propertiesByCategoryLock = new Object(); public DescriptorImpl() { super(ThrottleJobProperty.class); - LOGGER.log(Level.INFO, "DescriptorImpl(): "); - load(); + synchronized(propertiesByCategoryLock) { + load(); + // Explictly handle the persisted data from the version 1.8.1 + if (propertiesByCategory == null) { + propertiesByCategory = new HashMap>(); + } + if (!propertiesByCategory.isEmpty()) { + propertiesByCategory.clear(); + save(); // Save the configuration to remove obsolete data + } + } } @Override @@ -241,6 +288,10 @@ public String getDisplayName() { public boolean isApplicable(Class jobType) { return Job.class.isAssignableFrom(jobType) && Queue.Task.class.isAssignableFrom(jobType); } + + public boolean isMatrixProject(Job job) { + return job instanceof MatrixProject; + } @Override public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { @@ -288,12 +339,12 @@ public ThrottleCategory getCategoryByName(String categoryName) { } public void setCategories(List categories) { - this.categories = categories; + this.categories = new CopyOnWriteArrayList(categories); } public List getCategories() { if (categories == null) { - categories = new ArrayList(); + categories = new CopyOnWriteArrayList(); } return categories; @@ -402,6 +453,4 @@ public String getDisplayName() { } } } - - private static final Logger LOGGER = Logger.getLogger(ThrottleQueueTaskDispatcher.class.getName()); } diff --git a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java index a4be8174..8f22de38 100644 --- a/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java +++ b/src/main/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcher.java @@ -26,18 +26,21 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; @Extension public class ThrottleQueueTaskDispatcher extends QueueTaskDispatcher { @Override - public CauseOfBlockage canTake(Node node, Queue.BuildableItem item) { - Task task = item.task; - if (task instanceof MatrixConfiguration) { + public CauseOfBlockage canTake(Node node, Task task) { + + ThrottleJobProperty tjp = getThrottleJobProperty(task); + + // Handle multi-configuration filters + if (!shouldBeThrottled(task, tjp)) { return null; } - ThrottleJobProperty tjp = getThrottleJobProperty(task); if (tjp!=null && tjp.getThrottleEnabled()) { CauseOfBlockage cause = canRun(task, tjp); if (cause != null) return cause; @@ -91,7 +94,7 @@ else if (tjp.getThrottleOption().equals("category")) { return null; } - // @Override on jenkins 1.427+ , but still compatible with 1.399 + // @Override on jenkins 4.127+ , but still compatible with 1.399 public CauseOfBlockage canRun(Queue.Item item) { ThrottleJobProperty tjp = getThrottleJobProperty(item.task); if (tjp!=null && tjp.getThrottleEnabled()) { @@ -103,8 +106,34 @@ public CauseOfBlockage canRun(Queue.Item item) { return null; } + @Nonnull + private ThrottleMatrixProjectOptions getMatrixOptions(Task task) { + ThrottleJobProperty tjp = getThrottleJobProperty(task); + if (tjp == null) return ThrottleMatrixProjectOptions.DEFAULT; + ThrottleMatrixProjectOptions matrixOptions = tjp.getMatrixOptions(); + return matrixOptions != null ? matrixOptions : ThrottleMatrixProjectOptions.DEFAULT; + } + + private boolean shouldBeThrottled(@Nonnull Task task, @CheckForNull ThrottleJobProperty tjp) { + if (tjp == null) return false; + if (!tjp.getThrottleEnabled()) return false; + + // Handle matrix options + ThrottleMatrixProjectOptions matrixOptions = tjp.getMatrixOptions(); + if (matrixOptions == null) matrixOptions = ThrottleMatrixProjectOptions.DEFAULT; + if (!matrixOptions.isThrottleMatrixConfigurations() && task instanceof MatrixConfiguration) { + return false; + } + if (!matrixOptions.isThrottleMatrixBuilds()&& task instanceof MatrixProject) { + return false; + } + + // Allow throttling by default + return true; + } + public CauseOfBlockage canRun(Task task, ThrottleJobProperty tjp) { - if (task instanceof MatrixConfiguration) { + if (!shouldBeThrottled(task, tjp)) { return null; } if (Hudson.getInstance().getQueue().isPending(task)) { @@ -272,6 +301,10 @@ private ThrottleJobProperty getThrottleJobProperty(Task task) { } private int buildsOfProjectOnNode(Node node, Task task) { + if (!shouldBeThrottled(task, getThrottleJobProperty(task))) { + return 0; + } + int runCount = 0; LOGGER.log(Level.FINE, "Checking for builds of {0} on node {1}", new Object[] {task.getName(), node.getDisplayName()}); @@ -279,13 +312,13 @@ private int buildsOfProjectOnNode(Node node, Task task) { // a build right after it was launched, for some reason. Computer computer = node.toComputer(); if (computer != null) { //Not all nodes are certain to become computers, like nodes with 0 executors. - for (Executor e : computer.getExecutors()) { - runCount += buildsOnExecutor(task, e); - } - if (task instanceof MatrixProject) { + // Count flyweight tasks that might not consume an actual executor. for (Executor e : computer.getOneOffExecutors()) { runCount += buildsOnExecutor(task, e); } + + for (Executor e : computer.getExecutors()) { + runCount += buildsOnExecutor(task, e); } } @@ -346,5 +379,5 @@ private int getMaxConcurrentPerNodeBasedOnMatchingLabels( return maxConcurrentPerNodeLabeledIfMatch; } - private static final Logger LOGGER = Logger.getLogger(ThrottleJobProperty.class.getName()); + private static final Logger LOGGER = Logger.getLogger(ThrottleQueueTaskDispatcher.class.getName()); } diff --git a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java index 19bbb5bc..09a7cd4f 100644 --- a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java +++ b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleJobPropertyTest.java @@ -6,16 +6,16 @@ import hudson.model.Queue; import hudson.security.ACL; import hudson.security.AuthorizationStrategy; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.HudsonTestCase; +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; + public class ThrottleJobPropertyTest extends HudsonTestCase { private static final String THROTTLE_OPTION_CATEGORY = "category"; // TODO move this into ThrottleJobProperty and use consistently; same for "project" + private final Random random = new Random(System.currentTimeMillis()); @Bug(19623) public void testGetCategoryProjects() throws Exception { @@ -41,6 +41,95 @@ public void testGetCategoryProjects() throws Exception { p3.removeProperty(ThrottleJobProperty.class); assertProjects(beta, p3b); } + + + + public void testToString_withNulls(){ + ThrottleJobProperty tjp = new ThrottleJobProperty(0,0, null, false, null, false, "", ThrottleMatrixProjectOptions.DEFAULT); + assertNotNull(tjp.toString()); + } + + public void testThrottleJob_constructor_should_store_arguments() { + Integer expectedMaxConcurrentPerNode = anyInt(); + Integer expectedMaxConcurrentTotal = anyInt(); + List expectedCategories = Collections.emptyList(); + boolean expectedThrottleEnabled = anyBoolean(); + String expectedThrottleOption = anyString(); + boolean expectedLimitOneJobWithMatchingParams = anyBoolean(); + String expectedParamsToUseForLimit = anyString(); + + ThrottleJobProperty property = new ThrottleJobProperty(expectedMaxConcurrentPerNode, + expectedMaxConcurrentTotal, + expectedCategories, expectedThrottleEnabled, expectedThrottleOption, + expectedLimitOneJobWithMatchingParams, expectedParamsToUseForLimit, + ThrottleMatrixProjectOptions.DEFAULT); + + assertEquals(expectedMaxConcurrentPerNode, property.getMaxConcurrentPerNode()); + assertEquals(expectedMaxConcurrentTotal, property.getMaxConcurrentTotal()); + assertEquals(expectedCategories, property.getCategories()); + assertEquals(expectedThrottleEnabled, property.getThrottleEnabled()); + assertEquals(expectedThrottleOption, property.getThrottleOption()); + } + + public void testThrottleJob_should_copy_categories_to_concurrency_safe_list() { + final String category = anyString(); + + ArrayList unsafeList = new ArrayList() {{ + add(category); + }}; + + ThrottleJobProperty property = new ThrottleJobProperty(anyInt(), + anyInt(), + unsafeList, + anyBoolean(), + "throttle_option", + anyBoolean(), + anyString(), + ThrottleMatrixProjectOptions.DEFAULT); + + List storedCategories = property.getCategories(); + assertEquals("contents of original and stored list should be the equal", unsafeList, storedCategories); + assertTrue("expected unsafe list to be converted to a converted to some other concurrency-safe impl", + unsafeList != storedCategories); + assertTrue(storedCategories instanceof CopyOnWriteArrayList); + } + + public void testThrottleJob_constructor_handles_null_categories(){ + ThrottleJobProperty property = new ThrottleJobProperty(anyInt(), + anyInt(), + null, + anyBoolean(), + "throttle_option", + anyBoolean(), + anyString(), + ThrottleMatrixProjectOptions.DEFAULT); + + assertEquals(Collections.emptyList(), property.getCategories()); + } + + public void testDescriptorImpl_should_a_concurrency_safe_list_for_categories(){ + ThrottleJobProperty.DescriptorImpl descriptor = new ThrottleJobProperty.DescriptorImpl(); + + assertTrue(descriptor.getCategories() instanceof CopyOnWriteArrayList); + + final ThrottleJobProperty.ThrottleCategory category = new ThrottleJobProperty.ThrottleCategory( + anyString(), anyInt(), anyInt(), null); + + ArrayList unsafeList = + new ArrayList() {{ + add(category); + }}; + + + descriptor.setCategories(unsafeList); + List storedCategories = descriptor.getCategories(); + assertEquals("contents of original and stored list should be the equal", unsafeList, storedCategories); + assertTrue("expected unsafe list to be converted to a converted to some other concurrency-safe impl", + unsafeList != storedCategories); + assertTrue(storedCategories instanceof CopyOnWriteArrayList); + } + + private void assertProjects(String category, AbstractProject... projects) { jenkins.setAuthorizationStrategy(new RejectAllAuthorizationStrategy()); try { @@ -64,5 +153,16 @@ private static class RejectAllAuthorizationStrategy extends AuthorizationStrateg } } + private String anyString() { + return "concurrency_" + anyInt(); + } + + private boolean anyBoolean() { + return random.nextBoolean(); + } + + private int anyInt() { + return random.nextInt(10000); + } } diff --git a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcherTest.java b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcherTest.java index ecc94851..529a1870 100644 --- a/src/test/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcherTest.java +++ b/src/test/java/hudson/plugins/throttleconcurrents/ThrottleQueueTaskDispatcherTest.java @@ -358,7 +358,7 @@ private String configureLogger() input.setValueAttribute(logger); } HtmlSelect select = form.getSelectByName("level"); - HtmlOption option = select.getOptionByValue("FINE"); + HtmlOption option = select.getOptionByValue("fine"); select.setSelectedAttribute(option, true); break; }