Skip to content

Commit

Permalink
[JENKINS-29790] Noting merge of #1783.
Browse files Browse the repository at this point in the history
  • Loading branch information
jglick committed Aug 24, 2015
2 parents 5e9492d + c26fd23 commit 434608b
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 25 deletions.
4 changes: 3 additions & 1 deletion changelog.html
Expand Up @@ -55,7 +55,9 @@
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=>
<li class="major bug">
Race condition in triggers could cause various <code>NullPointerException</code>s.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-29790">issue 29790</a>)
</ul>
</div><!--=TRUNK-END=-->
<h3><a name=v1.626>What's new in 1.626</a> (2015/08/23)</h3>
Expand Down
10 changes: 6 additions & 4 deletions core/src/main/java/hudson/scheduler/CronTabList.java
Expand Up @@ -29,6 +29,8 @@
import java.util.Collection;
import java.util.Vector;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

Expand All @@ -44,7 +46,7 @@ public final class CronTabList {
private final Vector<CronTab> tabs;

public CronTabList(Collection<CronTab> tabs) {
this.tabs = new Vector<CronTab>(tabs);
this.tabs = new Vector<>(tabs);
}

/**
Expand Down Expand Up @@ -90,12 +92,12 @@ public String checkSanity() {
return null;
}

public static CronTabList create(String format) throws ANTLRException {
public static CronTabList create(@Nonnull String format) throws ANTLRException {
return create(format,null);
}

public static CronTabList create(String format, Hash hash) throws ANTLRException {
Vector<CronTab> r = new Vector<CronTab>();
public static CronTabList create(@Nonnull String format, Hash hash) throws ANTLRException {
Vector<CronTab> r = new Vector<>();
int lineNumber = 0;
String timezone = null;

Expand Down
28 changes: 22 additions & 6 deletions core/src/main/java/hudson/triggers/SCMTrigger.java
Expand Up @@ -2,6 +2,7 @@
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Brian Westrich, Jean-Baptiste Quenot, id:cactusman
* 2015 Kanstantsin Shautsou
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -24,6 +25,7 @@
package hudson.triggers;

import antlr.ANTLRException;
import com.google.common.base.Preconditions;
import hudson.Extension;
import hudson.Util;
import hudson.console.AnnotatedLargeText;
Expand Down Expand Up @@ -117,6 +119,10 @@ public boolean isIgnorePostCommitHooks() {

@Override
public void run() {
if (job == null) {
return;
}

run(null);
}

Expand All @@ -128,6 +134,10 @@ public void run() {
* @since 1.375
*/
public void run(Action[] additionalActions) {
if (job == null) {
return;
}

DescriptorImpl d = getDescriptor();

LOGGER.fine("Scheduling a polling for "+job);
Expand All @@ -152,6 +162,10 @@ public DescriptorImpl getDescriptor() {

@Override
public Collection<? extends Action> getProjectActions() {
if (job == null) {
return Collections.emptyList();
}

return Collections.singleton(new SCMAction());
}

Expand Down Expand Up @@ -457,10 +471,12 @@ public class Runner implements Runnable {
private Action[] additionalActions;

public Runner() {
additionalActions = new Action[0];
this(null);
}

public Runner(Action[] actions) {
Preconditions.checkNotNull(job, "Runner can't be instantiated when job is null");

if (actions == null) {
additionalActions = new Action[0];
} else {
Expand Down Expand Up @@ -514,11 +530,7 @@ private boolean runPolling() {
else
logger.println("No changes");
return result;
} catch (Error e) {
e.printStackTrace(listener.error("Failed to record SCM polling for "+job));
LOGGER.log(Level.SEVERE,"Failed to record SCM polling for "+job,e);
throw e;
} catch (RuntimeException e) {
} catch (Error | RuntimeException e) {
e.printStackTrace(listener.error("Failed to record SCM polling for "+job));
LOGGER.log(Level.SEVERE,"Failed to record SCM polling for "+job,e);
throw e;
Expand All @@ -532,6 +544,10 @@ private boolean runPolling() {
}

public void run() {
if (job == null) {
return;
}

String threadName = Thread.currentThread().getName();
Thread.currentThread().setName("SCM polling for "+job);
try {
Expand Down
9 changes: 8 additions & 1 deletion core/src/main/java/hudson/triggers/TimerTrigger.java
Expand Up @@ -2,6 +2,7 @@
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Jean-Baptiste Quenot, Martin Eigenbrodt
* 2015 Kanstantsin Shautsou
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -38,6 +39,8 @@
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;

import javax.annotation.Nonnull;

/**
* {@link Trigger} that runs a job periodically.
*
Expand All @@ -46,12 +49,16 @@
public class TimerTrigger extends Trigger<BuildableItem> {

@DataBoundConstructor
public TimerTrigger(String spec) throws ANTLRException {
public TimerTrigger(@Nonnull String spec) throws ANTLRException {
super(spec);
}

@Override
public void run() {
if (job == null) {
return;
}

job.scheduleBuild(0, new TimerTriggerCause());
}

Expand Down
10 changes: 7 additions & 3 deletions core/src/main/java/hudson/triggers/Trigger.java
Expand Up @@ -2,7 +2,8 @@
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Brian Westrich, Jean-Baptiste Quenot, Stephen Connolly, Tom Huybrechts
*
* 2015 Kanstantsin Shautsou
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
Expand Down Expand Up @@ -57,6 +58,8 @@

import antlr.ANTLRException;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import hudson.model.Items;
import jenkins.model.ParameterizedJobMixIn;
Expand Down Expand Up @@ -149,14 +152,15 @@ public TriggerDescriptor getDescriptor() {

protected final String spec;
protected transient CronTabList tabs;
@CheckForNull
protected transient J job;

/**
* Creates a new {@link Trigger} that gets {@link #run() run}
* periodically. This is useful when your trigger does
* some polling work.
*/
protected Trigger(String cronTabSpec) throws ANTLRException {
protected Trigger(@Nonnull String cronTabSpec) throws ANTLRException {
this.spec = cronTabSpec;
this.tabs = CronTabList.create(cronTabSpec);
}
Expand Down Expand Up @@ -316,7 +320,7 @@ public static DescriptorExtensionList<Trigger<?>,TriggerDescriptor> all() {
* Returns a subset of {@link TriggerDescriptor}s that applys to the given item.
*/
public static List<TriggerDescriptor> for_(Item i) {
List<TriggerDescriptor> r = new ArrayList<TriggerDescriptor>();
List<TriggerDescriptor> r = new ArrayList<>();
for (TriggerDescriptor t : all()) {
if(!t.isApplicable(i)) continue;

Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/hudson/util/SequentialExecutionQueue.java
@@ -1,5 +1,6 @@
package hudson.util;

import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -58,7 +59,7 @@ public synchronized void setExecutors(ExecutorService svc) {
}


public synchronized void execute(Runnable item) {
public synchronized void execute(@Nonnull Runnable item) {
QueueEntry e = entries.get(item);
if(e==null) {
e = new QueueEntry(item);
Expand Down
13 changes: 8 additions & 5 deletions core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java
Expand Up @@ -72,6 +72,8 @@
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;

import javax.annotation.Nonnull;

/**
* Like {@link BuildTrigger} but defined on the downstream project.
* Operates via {@link BuildTrigger#execute} and {@link DependencyGraph},
Expand All @@ -84,12 +86,13 @@
public final class ReverseBuildTrigger extends Trigger<Job> implements DependencyDeclarer {

private static final Logger LOGGER = Logger.getLogger(ReverseBuildTrigger.class.getName());
private static final Map<Job,Collection<ReverseBuildTrigger>> upstream2Trigger = new WeakHashMap<Job,Collection<ReverseBuildTrigger>>();
private static final Map<Job,Collection<ReverseBuildTrigger>> upstream2Trigger = new WeakHashMap<>();

private String upstreamProjects;
private final Result threshold;

@DataBoundConstructor public ReverseBuildTrigger(String upstreamProjects, Result threshold) {
@DataBoundConstructor
public ReverseBuildTrigger(String upstreamProjects, Result threshold) {
this.upstreamProjects = upstreamProjects;
this.threshold = threshold;
}
Expand Down Expand Up @@ -155,7 +158,7 @@ private boolean shouldTrigger(Run upstreamBuild, TaskListener listener) {
synchronized (upstream2Trigger) {
Collection<ReverseBuildTrigger> triggers = upstream2Trigger.get(upstream);
if (triggers == null) {
triggers = new LinkedList<ReverseBuildTrigger>();
triggers = new LinkedList<>();
upstream2Trigger.put(upstream, triggers);
}
triggers.remove(this);
Expand Down Expand Up @@ -222,14 +225,14 @@ public FormValidation doCheckUpstreamProjects(@AncestorInPath Job project, @Quer
}

@Extension public static final class RunListenerImpl extends RunListener<Run> {
@Override public void onCompleted(Run r, TaskListener listener) {
@Override public void onCompleted(@Nonnull Run r, @Nonnull TaskListener listener) {
Collection<ReverseBuildTrigger> triggers;
synchronized (upstream2Trigger) {
Collection<ReverseBuildTrigger> _triggers = upstream2Trigger.get(r.getParent());
if (_triggers == null || _triggers.isEmpty()) {
return;
}
triggers = new ArrayList<ReverseBuildTrigger>(_triggers);
triggers = new ArrayList<>(_triggers);
}
for (final ReverseBuildTrigger trigger : triggers) {
if (trigger.shouldTrigger(r, listener)) {
Expand Down
42 changes: 42 additions & 0 deletions core/src/test/java/hudson/triggers/SCMTriggerTest.java
@@ -0,0 +1,42 @@
/*
* The MIT License
*
* Copyright (c) 2015 Kanstantsin Shautsou
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.triggers;

import org.junit.Test;
import org.jvnet.hudson.test.Issue;

/**
* @author Kanstantsin Shautsou
*/
public class SCMTriggerTest {
@Issue({"JENKINS-29790", "JENKINS-29945"})
@Test
public void testNoNPE() throws Exception {
final SCMTrigger scmTrigger = new SCMTrigger("");

scmTrigger.run();
scmTrigger.run(null);
scmTrigger.getProjectActions();
}
}
39 changes: 39 additions & 0 deletions core/src/test/java/hudson/triggers/TimerTriggerTest.java
@@ -0,0 +1,39 @@
/*
* The MIT License
*
* Copyright (c) 2015 Kanstantsin Shautsou
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.triggers;

import antlr.ANTLRException;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;

/**
* @author Kanstantsin Shautsou
*/
public class TimerTriggerTest {
@Issue("JENKINS-29790")
@Test
public void testNoNPE() throws ANTLRException {
new TimerTrigger("").run();
}
}
3 changes: 2 additions & 1 deletion pom.xml
Expand Up @@ -101,6 +101,7 @@ THE SOFTWARE.
<matrix-project.version>1.4.1</matrix-project.version>
<animal.sniffer.skip>${skipTests}</animal.sniffer.skip>
<findbugs-maven-plugin.version>3.0.1</findbugs-maven-plugin.version>
<test-annotations.version>1.2</test-annotations.version>

<java.level>7</java.level>
</properties>
Expand Down Expand Up @@ -243,7 +244,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci</groupId>
<artifactId>test-annotations</artifactId>
<version>1.1</version>
<version>${test-annotations.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
4 changes: 2 additions & 2 deletions test/pom.xml
Expand Up @@ -108,9 +108,9 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci</groupId>
<artifactId>test-annotations</artifactId>
<version>1.1</version>
<scope>compile</scope>
<version>${test-annotations.version}</version>
<!-- in this module we need this as a compile scope, whereas in the parent it's test -->
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jvnet.mock-javamail</groupId>
Expand Down
2 changes: 1 addition & 1 deletion test/src/test/java/hudson/PluginTest.java
Expand Up @@ -34,7 +34,7 @@ public class PluginTest {

@Rule public JenkinsRule r = new JenkinsRule();

@Issue("SECURITY-131") // TODO test-annotations 1.2+: @Issue({"SECURITY-131", "SECURITY-155"})
@Issue({"SECURITY-131", "SECURITY-155"})
@Test public void doDynamic() throws Exception {
r.createWebClient().goTo("plugin/credentials/images/24x24/credentials.png", "image/png");
/* Collapsed somewhere before it winds up in restOfPath:
Expand Down

0 comments on commit 434608b

Please sign in to comment.