Skip to content

Commit cae4704

Browse files
authored
Merge pull request #184 from chrisLeeTW/feature/thread-pools
feat: use thread pool for trigger job queue to prevent overloading.
2 parents cc5ba8b + 23d67ce commit cae4704

File tree

4 files changed

+148
-99
lines changed

4 files changed

+148
-99
lines changed
Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
22

3-
import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.cloud.CloudBitbucketCause;
43
import hudson.Extension;
5-
import hudson.model.AbstractBuild;
64
import hudson.model.Job;
75
import hudson.model.Run;
86
import hudson.model.TaskListener;
@@ -21,7 +19,7 @@ public class BitbucketBuildListener extends RunListener<Run<?, ?>> {
2119
private static final Logger logger = Logger.getLogger(BitbucketBuildListener.class.getName());
2220

2321
@Override
24-
public void onStarted(Run r, TaskListener listener) {
22+
public void onStarted(Run<?,?> r, TaskListener listener) {
2523
logger.fine("BitbucketBuildListener onStarted called.");
2624
BitbucketBuilds builds = builds(r);
2725
if (builds != null) {
@@ -30,7 +28,7 @@ public void onStarted(Run r, TaskListener listener) {
3028
}
3129

3230
@Override
33-
public void onCompleted(Run r, @Nonnull TaskListener listener) {
31+
public void onCompleted(Run<?,?> r, @Nonnull TaskListener listener) {
3432
logger.fine("BitbucketBuildListener onCompleted called.");
3533
BitbucketBuilds builds = builds(r);
3634
if (builds != null) {
@@ -40,20 +38,16 @@ public void onCompleted(Run r, @Nonnull TaskListener listener) {
4038

4139
private BitbucketBuilds builds(Run<?, ?> r) {
4240
BitbucketBuildTrigger trigger = null;
43-
if (r instanceof AbstractBuild) {
44-
trigger = BitbucketBuildTrigger.getTrigger(((AbstractBuild) r).getProject());
45-
} else {
46-
Job job = r.getParent();
47-
if (job instanceof ParameterizedJobMixIn.ParameterizedJob) {
48-
49-
for (Trigger<?> t : ((ParameterizedJobMixIn.ParameterizedJob) job).getTriggers().values()) {
50-
if (t instanceof BitbucketBuildTrigger) {
51-
trigger = (BitbucketBuildTrigger) t;
52-
}
41+
42+
Job<?,?> job = r.getParent();
43+
if (job instanceof ParameterizedJobMixIn.ParameterizedJob) {
44+
for (Trigger<?> t : ((ParameterizedJobMixIn.ParameterizedJob) job).getTriggers().values()) {
45+
if (t instanceof BitbucketBuildTrigger) {
46+
trigger = (BitbucketBuildTrigger) t;
5347
}
5448
}
5549
}
50+
5651
return trigger == null ? null : trigger.getBuilder().getBuilds();
5752
}
58-
5953
}

src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java

Lines changed: 103 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,64 @@
11
package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
22

3-
import antlr.ANTLRException;
3+
import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf;
4+
5+
import java.io.IOException;
6+
import java.net.URISyntaxException;
7+
import java.util.ArrayList;
8+
import java.util.HashMap;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.concurrent.ExecutorService;
12+
import java.util.concurrent.Executors;
13+
import java.util.logging.Level;
14+
import java.util.logging.Logger;
15+
16+
import javax.annotation.Nonnull;
17+
import javax.annotation.Nullable;
18+
419
import com.cloudbees.plugins.credentials.CredentialsProvider;
520
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
621
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
722
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
23+
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
24+
25+
import org.apache.commons.lang.StringUtils;
26+
import org.eclipse.jgit.transport.URIish;
27+
import org.jenkinsci.Symbol;
28+
import org.kohsuke.stapler.DataBoundConstructor;
29+
import org.kohsuke.stapler.StaplerRequest;
30+
31+
import antlr.ANTLRException;
832
import hudson.Extension;
9-
import hudson.model.*;
33+
import hudson.model.Cause;
34+
import hudson.model.CauseAction;
35+
import hudson.model.Executor;
36+
import hudson.model.Item;
37+
import hudson.model.Job;
38+
import hudson.model.ParameterDefinition;
39+
import hudson.model.ParameterValue;
40+
import hudson.model.ParametersAction;
41+
import hudson.model.ParametersDefinitionProperty;
1042
import hudson.model.Queue;
43+
import hudson.model.Result;
44+
import hudson.model.Run;
1145
import hudson.model.queue.QueueTaskFuture;
1246
import hudson.plugins.git.RevisionParameterAction;
47+
import hudson.security.ACL;
1348
import hudson.triggers.Trigger;
1449
import hudson.triggers.TriggerDescriptor;
1550
import hudson.util.ListBoxModel;
1651
import jenkins.model.Jenkins;
1752
import jenkins.model.ParameterizedJobMixIn;
1853
import net.sf.json.JSONObject;
19-
import org.apache.commons.lang.StringUtils;
20-
import org.eclipse.jgit.transport.URIish;
21-
import org.jenkinsci.Symbol;
22-
import org.kohsuke.stapler.DataBoundConstructor;
23-
import org.kohsuke.stapler.StaplerRequest;
24-
25-
import java.io.IOException;
26-
import java.net.URISyntaxException;
27-
import java.util.ArrayList;
28-
import java.util.HashMap;
29-
import java.util.List;
30-
import java.util.Map;
31-
import java.util.logging.Level;
32-
import java.util.logging.Logger;
33-
import javax.annotation.Nonnull;
34-
import javax.annotation.Nullable;
35-
36-
import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf;
3754

3855
/**
3956
* Created by nishio
4057
*/
4158
public class BitbucketBuildTrigger extends Trigger<Job<?, ?>> {
4259
private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName());
60+
private static final ExecutorService pool = Executors.newFixedThreadPool(5);
61+
4362
private final String projectPath;
4463
private final String bitbucketServer;
4564
private final String cron;
@@ -165,6 +184,7 @@ public boolean getApproveIfSuccess() {
165184
public boolean getCancelOutdatedJobs() {
166185
return cancelOutdatedJobs;
167186
}
187+
168188
/**
169189
* @return a phrase that when entered in a comment will trigger a new build
170190
*/
@@ -173,38 +193,35 @@ public String getCommentTrigger() {
173193
}
174194

175195
@Override
176-
public void start(Job<?, ?> project, boolean newInstance) {
196+
public void start(Job<?, ?> job, boolean newInstance) {
197+
super.start(job, newInstance);
198+
177199
try {
178200
this.bitbucketPullRequestsBuilder = BitbucketPullRequestsBuilder.getBuilder();
179-
this.bitbucketPullRequestsBuilder.setProject(project);
201+
this.bitbucketPullRequestsBuilder.setJob(job);
180202
this.bitbucketPullRequestsBuilder.setTrigger(this);
181203
this.bitbucketPullRequestsBuilder.setupBuilder();
182-
} catch(IllegalStateException e) {
204+
} catch(Exception e) {
183205
logger.log(Level.SEVERE, "Can't start trigger", e);
184206
return;
185207
}
186-
super.start(project, newInstance);
187208
}
188209

189-
public static BitbucketBuildTrigger getTrigger(AbstractProject project) {
190-
Trigger trigger = project.getTrigger(BitbucketBuildTrigger.class);
210+
public static BitbucketBuildTrigger getTrigger(Job<?, ?> job) {
211+
if (!(job instanceof ParameterizedJobMixIn.ParameterizedJob)) {
212+
return null;
213+
}
214+
215+
ParameterizedJobMixIn.ParameterizedJob pjob = (ParameterizedJobMixIn.ParameterizedJob) job;
216+
217+
Trigger<?> trigger = pjob.getTriggers().get(descriptor);
191218
return (BitbucketBuildTrigger)trigger;
192219
}
193220

194221
public BitbucketPullRequestsBuilder getBuilder() {
195222
return this.bitbucketPullRequestsBuilder;
196223
}
197224

198-
private ParameterizedJobMixIn retrieveScheduleJob(final Job<?, ?> job) {
199-
// TODO 1.621+ use standard method
200-
return new ParameterizedJobMixIn() {
201-
@Override
202-
protected Job asJob() {
203-
return job;
204-
}
205-
};
206-
}
207-
208225
public QueueTaskFuture<?> startJob(BitbucketCause cause) {
209226
Map<String, ParameterValue> values = this.getDefaultParameters();
210227

@@ -213,10 +230,19 @@ public QueueTaskFuture<?> startJob(BitbucketCause cause) {
213230
abortRunningJobsThatMatch(cause);
214231
}
215232

216-
return retrieveScheduleJob(this.job).scheduleBuild2(0,
217-
new CauseAction(cause),
218-
new ParametersAction(new ArrayList(values.values())),
219-
new RevisionParameterAction(cause.getSourceCommitHash(), getBitbucketRepoUrl(cause.getRepositoryOwner(), cause.getRepositoryName())));
233+
ParameterizedJobMixIn scheduledJob = new ParameterizedJobMixIn() {
234+
@Override
235+
protected Job asJob() {
236+
return job;
237+
}
238+
};
239+
240+
return scheduledJob.scheduleBuild2(
241+
this.getInstance().getQuietPeriod(),
242+
new CauseAction(cause),
243+
new ParametersAction(new ArrayList<ParameterValue>(values.values())),
244+
new RevisionParameterAction(cause.getSourceCommitHash())
245+
);
220246
}
221247

222248
private URIish getBitbucketRepoUrl(String repoOwner, String repoName) {
@@ -252,7 +278,7 @@ private void abortRunningJobsThatMatch(@Nonnull BitbucketCause bitbucketCause) {
252278
logger.fine("Looking for running jobs that match PR ID: " + bitbucketCause.getPullRequestId());
253279
for (Object o : job.getBuilds()) {
254280
if (o instanceof Run) {
255-
Run build = (Run) o;
281+
Run<?,?> build = (Run<?,?>) o;
256282
if (build.isBuilding() && hasCauseFromTheSamePullRequest(build.getCauses(), bitbucketCause)) {
257283
logger.fine("Aborting build: " + build + " since PR is outdated");
258284
setBuildDescription(build);
@@ -266,7 +292,7 @@ private void abortRunningJobsThatMatch(@Nonnull BitbucketCause bitbucketCause) {
266292
}
267293
}
268294

269-
private void setBuildDescription(final Run build) {
295+
private void setBuildDescription(final Run<?,?> build) {
270296
try {
271297
build.setDescription("Aborting build by `Bitbucket Pullrequest Builder Plugin`: " + build + " since PR is outdated");
272298
} catch (IOException e) {
@@ -303,13 +329,17 @@ private Map<String, ParameterValue> getDefaultParameters() {
303329

304330
@Override
305331
public void run() {
306-
Job<?,?> project = this.getBuilder().getProject();
307-
if (project instanceof AbstractProject && ((AbstractProject)project).isDisabled()) {
308-
logger.fine("Build Skip.");
309-
} else {
310-
this.bitbucketPullRequestsBuilder.run();
332+
Job<?,?> job = this.getBuilder().getJob();
333+
String name = job.getFullName();
334+
335+
if (!job.isBuildable()) {
336+
logger.log(Level.FINE, "Build Skip for job - {0}.", name);
337+
} else {
338+
logger.log(Level.FINE, "running trigger for the job - {0}", name);
339+
340+
pool.submit(new TriggerRunnable(this.getBuilder()));
311341
this.getDescriptor().save();
312-
}
342+
}
313343
}
314344

315345
@Override
@@ -346,9 +376,31 @@ public boolean configure(StaplerRequest req, JSONObject json) throws FormExcepti
346376

347377
public ListBoxModel doFillCredentialsIdItems() {
348378
return new StandardListBoxModel()
349-
.withEmptySelection()
350-
.withMatching(instanceOf(UsernamePasswordCredentials.class),
351-
CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class));
379+
.withEmptySelection()
380+
.withMatching(
381+
instanceOf(UsernamePasswordCredentials.class),
382+
CredentialsProvider.lookupCredentials(
383+
StandardUsernamePasswordCredentials.class,
384+
(Item) null,
385+
ACL.SYSTEM,
386+
(DomainRequirement) null
387+
)
388+
);
389+
}
390+
}
391+
392+
private static final class TriggerRunnable implements Runnable {
393+
private final BitbucketPullRequestsBuilder builder;
394+
395+
TriggerRunnable(BitbucketPullRequestsBuilder builder) {
396+
this.builder = builder;
397+
}
398+
399+
@Override
400+
public void run() {
401+
synchronized (this) {
402+
this.builder.run();
403+
}
352404
}
353405
}
354406
}

src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
*/
2020
public class BitbucketPullRequestsBuilder {
2121
private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName());
22-
private Job<?, ?> project;
22+
private Job<?, ?> job;
2323
private BitbucketBuildTrigger trigger;
2424
private BitbucketRepository repository;
2525
private BitbucketBuilds builds;
@@ -33,14 +33,13 @@ public void stop() {
3333
}
3434

3535
public void run() {
36-
logger.fine("Build Start.");
3736
this.repository.init();
3837
Collection<AbstractPullrequest> targetPullRequests = this.repository.getTargetPullRequests();
3938
this.repository.addFutureBuildTasks(targetPullRequests);
4039
}
4140

4241
public BitbucketPullRequestsBuilder setupBuilder() {
43-
if (this.project == null || this.trigger == null) {
42+
if (this.job == null || this.trigger == null) {
4443
throw new IllegalStateException();
4544
}
4645
this.repository = new BitbucketRepository(this.trigger.getProjectPath(), this);
@@ -49,32 +48,32 @@ public BitbucketPullRequestsBuilder setupBuilder() {
4948
return this;
5049
}
5150

52-
public void setProject(Job<?, ?> project) {
53-
this.project = project;
51+
public void setJob(Job<?, ?> job) {
52+
this.job = job;
5453
}
5554

5655
public void setTrigger(BitbucketBuildTrigger trigger) {
5756
this.trigger = trigger;
5857
}
5958

60-
public Job<?, ?> getProject() {
61-
return this.project;
62-
}
63-
59+
public Job<?, ?> getJob() {
60+
return this.job;
61+
}
62+
6463
/**
6564
* Return MD5 hashed full project name or full project name, if MD5 hash provider inaccessible
6665
* @return unique project id
6766
*/
6867
public String getProjectId() {
6968
try {
7069
final MessageDigest MD5 = MessageDigest.getInstance("MD5");
71-
return new String(Hex.encodeHex(MD5.digest(this.project.getFullName().getBytes("UTF-8"))));
70+
return new String(Hex.encodeHex(MD5.digest(this.job.getFullName().getBytes("UTF-8"))));
7271
} catch (NoSuchAlgorithmException exc) {
7372
logger.log(Level.WARNING, "Failed to produce hash", exc);
7473
} catch (UnsupportedEncodingException exc) {
7574
logger.log(Level.WARNING, "Failed to produce hash", exc);
7675
}
77-
return this.project.getFullName();
76+
return this.job.getFullName();
7877

7978
}
8079

0 commit comments

Comments
 (0)