File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\tasks\BuildTrigger.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Brian Westrich, Martin Eigenbrodt
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.tasks;
25
26 import hudson.Extension;
27 import hudson.Launcher;
28 import hudson.Util;
29 import hudson.console.ModelHyperlinkNote;
30 import hudson.model.AbstractBuild;
31 import hudson.model.AbstractProject;
32 import hudson.model.Action;
33 import hudson.model.AutoCompletionCandidates;
34 import hudson.model.BuildListener;
35 import hudson.model.Cause.UpstreamCause;
36 import jenkins.model.DependencyDeclarer;
37 import hudson.model.DependencyGraph;
38 import hudson.model.DependencyGraph.Dependency;
39 import jenkins.model.Jenkins;
40 import hudson.model.Item;
41 import hudson.model.ItemGroup;
42 import hudson.model.Items;
43 import hudson.model.Job;
44 import hudson.model.Project;
45 import hudson.model.Result;
46 import hudson.model.Run;
47 import hudson.model.TaskListener;
48 import hudson.model.listeners.ItemListener;
49 import hudson.tasks.BuildTrigger.DescriptorImpl.ItemListenerImpl;
50 import hudson.util.FormValidation;
51 import net.sf.json.JSONObject;
52 import org.apache.commons.lang.StringUtils;
53 import org.kohsuke.stapler.AncestorInPath;
54 import org.kohsuke.stapler.DataBoundConstructor;
55 import org.kohsuke.stapler.QueryParameter;
56 import org.kohsuke.stapler.StaplerRequest;
57
58 import java.io.IOException;
59 import java.io.PrintStream;
60 import java.util.ArrayList;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.Comparator;
64 import java.util.List;
65 import java.util.StringTokenizer;
66 import java.util.logging.Level;
67 import java.util.logging.Logger;
68 import javax.annotation.CheckForNull;
69
70 /**
71 * Triggers builds of other projects.
72 *
73 * <p>
74 * Despite what the name suggests, this class doesn't actually trigger other jobs
75 * as a part of {@link #perform} method. Its main job is to simply augment
76 * {@link DependencyGraph}. Jobs are responsible for triggering downstream jobs
77 * on its own, because dependencies may come from other sources.
78 *
79 * <p>
80 * This class, however, does provide the {@link #execute(AbstractBuild, BuildListener, BuildTrigger)}
81 * method as a convenience method to invoke downstream builds.
82 *
83 * @author Kohsuke Kawaguchi
84 */
85 public class BuildTrigger extends Recorder implements DependencyDeclarer {
86
87 /**
88 * Comma-separated list of other projects to be scheduled.
89 */
90 private String childProjects;
91
92 /**
93 * Threshold status to trigger other builds.
94 *
95 * For compatibility reasons, this field could be null, in which case
96 * it should read as "SUCCESS".
97 */
98 private final Result threshold;
99
100 public BuildTrigger(String childProjects, boolean evenIfUnstable) {
101 this(childProjects,evenIfUnstable ? Result.UNSTABLE : Result.SUCCESS);
102 }
103
104 @DataBoundConstructor
105 public BuildTrigger(String childProjects, String threshold) {
106 this(childProjects, Result.fromString(StringUtils.defaultString(threshold, Result.SUCCESS.toString())));
107 }
108
109 public BuildTrigger(String childProjects, Result threshold) {
110 if(childProjects==null)
111 throw new IllegalArgumentException();
112 this.childProjects = childProjects;
113 this.threshold = threshold;
114 }
115
116 public BuildTrigger(List<AbstractProject> childProjects, Result threshold) {
117 this((Collection<AbstractProject>)childProjects,threshold);
118 }
119
120 public BuildTrigger(Collection<? extends AbstractProject> childProjects, Result threshold) {
121 this(Items.toNameList(childProjects),threshold);
122 }
123
124 public String getChildProjectsValue() {
125 return childProjects;
126 }
127
128 public Result getThreshold() {
129 if(threshold==null)
130 return Result.SUCCESS;
131 else
132 return threshold;
133 }
134
135 /**
136 * @deprecated as of 1.406
137 * Use {@link #getChildProjects(ItemGroup)}
138 */
139 public List<AbstractProject> getChildProjects() {
140 return getChildProjects(Jenkins.getInstance());
141 }
142
143 public List<AbstractProject> getChildProjects(AbstractProject owner) {
144 return getChildProjects(owner==null?null:owner.getParent());
145 }
146
147 public List<AbstractProject> getChildProjects(ItemGroup base) {
148 return Items.fromNameList(base,childProjects,AbstractProject.class);
149 }
150
151 public BuildStepMonitor getRequiredMonitorService() {
152 return BuildStepMonitor.NONE;
153 }
154
155 /**
156 * Checks if this trigger has the exact same set of children as the given list.
157 */
158 public boolean hasSame(AbstractProject owner, Collection<? extends AbstractProject> projects) {
159 List<AbstractProject> children = getChildProjects(owner);
160 return children.size()==projects.size() && children.containsAll(projects);
161 }
162
163 /**
164 * @deprecated as of 1.406
165 * Use {@link #hasSame(AbstractProject, Collection)}
166 */
167 public boolean hasSame(Collection<? extends AbstractProject> projects) {
168 return hasSame(null,projects);
169 }
170
171 @Override
172 public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) {
173 return true;
174 }
175
176 /**
177 * @deprecated since 1.341; use {@link #execute(AbstractBuild,BuildListener)}
178 */
179 @Deprecated
180 public static boolean execute(AbstractBuild build, BuildListener listener, BuildTrigger trigger) {
181 return execute(build, listener);
182 }
183
184 /**
185 * Convenience method to trigger downstream builds.
186 *
187 * @param build
188 * The current build. Its downstreams will be triggered.
189 * @param listener
190 * Receives the progress report.
191 */
192 public static boolean execute(AbstractBuild build, BuildListener listener) {
193 PrintStream logger = listener.getLogger();
194 // Check all downstream Project of the project, not just those defined by BuildTrigger
195 final DependencyGraph graph = Jenkins.getInstance().getDependencyGraph();
196 List<Dependency> downstreamProjects = new ArrayList<Dependency>(
197 graph.getDownstreamDependencies(build.getProject()));
198 // Sort topologically
199 Collections.sort(downstreamProjects, new Comparator<Dependency>() {
200 public int compare(Dependency lhs, Dependency rhs) {
201 // Swapping lhs/rhs to get reverse sort:
202 return graph.compare(rhs.getDownstreamProject(), lhs.getDownstreamProject());
203 }
204 });
205
206 for (Dependency dep : downstreamProjects) {
207 AbstractProject p = dep.getDownstreamProject();
208 if (p.isDisabled()) {
209 logger.println(Messages.BuildTrigger_Disabled(ModelHyperlinkNote.encodeTo(p)));
210 continue;
211 }
212 List<Action> buildActions = new ArrayList<Action>();
213 if (dep.shouldTriggerBuild(build, listener, buildActions)) {
214 // this is not completely accurate, as a new build might be triggered
215 // between these calls
216 String name = ModelHyperlinkNote.encodeTo(p)+" #"+p.getNextBuildNumber();
217 if(p.scheduleBuild(p.getQuietPeriod(), new UpstreamCause((Run)build),
218 buildActions.toArray(new Action[buildActions.size()]))) {
219 logger.println(Messages.BuildTrigger_Triggering(name));
220 } else {
221 logger.println(Messages.BuildTrigger_InQueue(name));
222 }
223 }
224 }
225
226 return true;
227 }
228
229 public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
230 for (AbstractProject p : getChildProjects(owner))
231 graph.addDependency(new Dependency(owner, p) {
232 @Override
233 public boolean shouldTriggerBuild(AbstractBuild build, TaskListener listener,
234 List<Action> actions) {
235 return build.getResult().isBetterOrEqualTo(threshold);
236 }
237 });
238 }
239
240 @Override
241 public boolean needsToRunAfterFinalized() {
242 return true;
243 }
244
245 /**
246 * Called from {@link ItemListenerImpl} when a job is renamed.
247 *
248 * @return true if this {@link BuildTrigger} is changed and needs to be saved.
249 */
250 public boolean onJobRenamed(String oldName, String newName) {
251 // quick test
252 if(!childProjects.contains(oldName))
253 return false;
254
255 boolean changed = false;
256
257 // we need to do this per string, since old Project object is already gone.
258 String[] projects = childProjects.split(",");
259 for( int i=0; i<projects.length; i++ ) {
260 if(projects[i].trim().equals(oldName)) {
261 projects[i] = newName;
262 changed = true;
263 }
264 }
265
266 if(changed) {
267 StringBuilder b = new StringBuilder();
268 for (String p : projects) {
269 if(b.length()>0) b.append(',');
270 b.append(p);
271 }
272 childProjects = b.toString();
273 }
274
275 return changed;
276 }
277
278 /**
279 * Correct broken data gracefully (#1537)
280 */
281 private Object readResolve() {
282 if(childProjects==null)
283 return childProjects="";
284 return this;
285 }
286
287 @Extension
288 public static class DescriptorImpl extends BuildStepDescriptor<Publisher> {
289 public String getDisplayName() {
290 return Messages.BuildTrigger_DisplayName();
291 }
292
293 @Override
294 public String getHelpFile() {
295 return "/help/project-config/downstream.html";
296 }
297
298 @Override
299 public BuildTrigger newInstance(StaplerRequest req, JSONObject formData) throws FormException {
300 String childProjectsString = formData.getString("childProjects").trim();
301 if (childProjectsString.endsWith(",")) {
302 childProjectsString = childProjectsString.substring(0, childProjectsString.length() - 1).trim();
303 }
304 return new BuildTrigger(
305 childProjectsString,
306 formData.optString("threshold", Result.SUCCESS.toString()));
307 }
308
309 @Override
310 public boolean isApplicable(Class<? extends AbstractProject> jobType) {
311 return true;
312 }
313
314 public boolean showEvenIfUnstableOption(@CheckForNull Class<? extends AbstractProject<?,?>> jobType) {
315 // UGLY: for promotion process, this option doesn't make sense.
316 return jobType == null || !jobType.getName().contains("PromotionProcess");
317 }
318
319 /**
320 * Form validation method.
321 */
322 public FormValidation doCheck(@AncestorInPath Item project, @QueryParameter String value, @QueryParameter boolean upstream) {
323 // Require CONFIGURE permission on this project
324 if(!project.hasPermission(Item.CONFIGURE)) return FormValidation.ok();
325
326 StringTokenizer tokens = new StringTokenizer(Util.fixNull(value),",");
327 boolean hasProjects = false;
328 while(tokens.hasMoreTokens()) {
329 String projectName = tokens.nextToken().trim();
330 if (StringUtils.isNotBlank(projectName)) {
331 Item item = Jenkins.getInstance().getItem(projectName,project,Item.class);
332 if(item==null)
333 return FormValidation.error(Messages.BuildTrigger_NoSuchProject(projectName,
334 AbstractProject.findNearest(projectName,project.getParent()).getRelativeNameFrom(project)));
335 if(!(item instanceof AbstractProject))
336 return FormValidation.error(Messages.BuildTrigger_NotBuildable(projectName));
337 if (!upstream && !item.hasPermission(Item.BUILD)) {
338 return FormValidation.error(Messages.BuildTrigger_you_have_no_permission_to_build_(projectName));
339 }
340 hasProjects = true;
341 }
342 }
343 if (!hasProjects) {
344 return FormValidation.error(Messages.BuildTrigger_NoProjectSpecified());
345 }
346
347 return FormValidation.ok();
348 }
349
350 public AutoCompletionCandidates doAutoCompleteChildProjects(@QueryParameter String value, @AncestorInPath Item self, @AncestorInPath ItemGroup container) {
351 return AutoCompletionCandidates.ofJobNames(Job.class,value,self,container);
352 }
353
354 @Extension
355 public static class ItemListenerImpl extends ItemListener {
356 @Override
357 public void onRenamed(Item item, String oldName, String newName) {
358 // update BuildTrigger of other projects that point to this object.
359 // can't we generalize this?
360 for( Project<?,?> p : Jenkins.getInstance().getAllItems(Project.class) ) {
361 BuildTrigger t = p.getPublishersList().get(BuildTrigger.class);
362 if(t!=null) {
363 if(t.onJobRenamed(oldName,newName)) {
364 try {
365 p.save();
366 } catch (IOException e) {
367 LOGGER.log(Level.WARNING, "Failed to persist project setting during rename from "+oldName+" to "+newName,e);
368 }
369 }
370 }
371 }
372 }
373 }
374 }
375
376 private static final Logger LOGGER = Logger.getLogger(BuildTrigger.class.getName());
377 }
378