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

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, id:cactusman
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.maven;
25
26 import com.google.common.collect.HashMultimap;
27 import com.google.common.collect.Multimap;
28 import hudson.CopyOnWrite;
29 import hudson.Functions;
30 import hudson.Util;
31 import hudson.maven.reporters.MavenMailer;
32 import hudson.model.AbstractProject;
33 import hudson.model.Action;
34 import hudson.model.DependencyGraph;
35 import hudson.model.Descriptor;
36 import hudson.model.Descriptor.FormException;
37 import hudson.model.Item;
38 import hudson.model.ItemGroup;
39 import hudson.model.JDK;
40 import hudson.model.Job;
41 import hudson.model.Label;
42 import hudson.model.Node;
43 import hudson.model.Resource;
44 import hudson.model.Saveable;
45 import hudson.tasks.LogRotator;
46 import hudson.tasks.Maven.MavenInstallation;
47 import hudson.tasks.Publisher;
48 import hudson.util.AlternativeUiTextProvider;
49 import hudson.util.DescribableList;
50 import jenkins.model.Jenkins;
51 import org.apache.maven.project.MavenProject;
52 import org.kohsuke.stapler.StaplerRequest;
53 import org.kohsuke.stapler.StaplerResponse;
54 import org.kohsuke.stapler.export.Exported;
55
56 import javax.servlet.ServletException;
57 import java.io.IOException;
58 import java.util.ArrayList;
59 import java.util.Collection;
60 import java.util.Collections;
61 import java.util.HashMap;
62 import java.util.HashSet;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Set;
66 import java.util.logging.Level;
67 import java.util.logging.Logger;
68
69 /**
70 * {@link Job} that builds projects based on Maven2.
71 *
72 * @author Kohsuke Kawaguchi
73 */
74 public class MavenModule extends AbstractMavenProject<MavenModule,MavenBuild> implements Saveable {
75 private DescribableList<MavenReporter,Descriptor<MavenReporter>> reporters =
76 new DescribableList<MavenReporter,Descriptor<MavenReporter>>(this);
77
78
79 /**
80 * Name taken from {@link MavenProject#getName()}.
81 */
82 private String displayName;
83
84 /**
85 * Version number of this module as of the last build, taken from {@link MavenProject#getVersion()}.
86 *
87 * This field can be null if Jenkins loaded old data
88 * that didn't record this information, so that situation
89 * needs to be handled gracefully.
90 *
91 * @since 1.199
92 */
93 private String version;
94
95 /**
96 * Packaging type of the module.
97 *
98 * pom, jar, maven-plugin, ejb, war, ear, rar, par or other custom types.
99 *
100 * @since 1.425
101 */
102 private String packaging;
103
104 private transient ModuleName moduleName;
105
106 /**
107 * @see PomInfo#relativePath
108 */
109 private String relativePath;
110
111 /**
112 * If this module has goals specified by itself.
113 * Otherwise leave it null to use the default goals specified in the parent.
114 */
115 private String goals;
116
117 /**
118 * List of modules that this module declares direct dependencies on.
119 */
120 @CopyOnWrite
121 private volatile Set<ModuleDependency> dependencies;
122
123 /**
124 * List of child modules as defined by &lt;module> POM element.
125 * Used to determine parent/child relationship of modules.
126 * <p>
127 * For compatibility reason, this field may be null when loading data from old hudson.
128 *
129 * @since 1.133
130 */
131 @CopyOnWrite
132 private volatile List<ModuleName> children;
133
134 /**
135 * Nest level used to display this module in the module list.
136 * The root module and orphaned module gets 0.
137 */
138 /*package*/ volatile transient int nestLevel;
139
140 /*package*/ MavenModule(MavenModuleSet parent, PomInfo pom, int firstBuildNumber) throws IOException {
141 super(parent, pom.name.toFileSystemName());
142 reconfigure(pom);
143 updateNextBuildNumber(firstBuildNumber);
144 }
145
146 /**
147 * {@link MavenModule} follows the same log rotation schedule as its parent.
148 */
149 @Override
150 public LogRotator getLogRotator() {
151 return getParent().getLogRotator();
152 }
153
154 /**
155 * @deprecated
156 * Not allowed to configure log rotation per module.
157 */
158 @Override
159 public void setLogRotator(LogRotator logRotator) {
160 throw new UnsupportedOperationException();
161 }
162
163 @Override
164 public boolean supportsLogRotator() {
165 return false;
166 }
167
168 @Override
169 public boolean isBuildable() {
170 // not buildable if the parent project is disabled
171 return super.isBuildable() && getParent().isBuildable();
172 }
173
174 /**
175 * Computes the list of {@link MavenModule}s that are 'under' this POM filesystem-wise. The list doens't include
176 * this module itself.
177 *
178 * <p>
179 * Note that this doesn't necessary has anything to do with the module inheritance structure or parent/child
180 * relationship of the POM.
181 */
182 public List<MavenModule> getSubsidiaries() {
183 List<MavenModule> r = new ArrayList<MavenModule>();
184 for (MavenModule mm : getParent().getModules())
185 if(mm!=this && mm.getRelativePath().startsWith(getRelativePath()))
186 r.add(mm);
187 return r;
188 }
189
190 /**
191 * Called to update the module with the new POM.
192 * <p>
193 * This method is invoked on {@link MavenModule} that has the matching
194 * {@link ModuleName}.
195 */
196 /*package*/ void reconfigure(PomInfo pom) {
197 this.displayName = pom.displayName;
198 this.version = pom.version;
199 this.packaging = pom.packaging;
200 this.relativePath = pom.relativePath;
201 this.dependencies = pom.dependencies;
202 this.children = pom.children;
203 this.nestLevel = pom.getNestLevel();
204 disabled = false;
205
206 if (pom.mailNotifier != null) {
207 MavenReporter reporter = getReporters().get(MavenMailer.class);
208 if (reporter != null) {
209 MavenMailer mailer = (MavenMailer) reporter;
210 mailer.dontNotifyEveryUnstableBuild = !pom.mailNotifier.isSendOnFailure();
211 String recipients = pom.mailNotifier.getConfiguration().getProperty("recipients");
212 if (recipients != null) {
213 mailer.recipients = recipients;
214 }
215 }
216 }
217 }
218
219 /**
220 * Returns if the given POM likely describes the same module with the same dependencies.
221 * Implementation needs not be 100% accurate in the true case, but it MUST return false
222 * if is not the same.
223 */
224 public boolean isSameModule(PomInfo pom) {
225 return pom.isSimilar(this.moduleName, this.dependencies);
226 }
227
228 @Override
229 protected void doSetName(String name) {
230 moduleName = ModuleName.fromFileSystemName(name);
231 super.doSetName(moduleName.toString());
232 }
233
234 @Override
235 public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOException {
236 super.onLoad(parent,name);
237 if(reporters==null)
238 reporters = new DescribableList<MavenReporter, Descriptor<MavenReporter>>(this);
239 reporters.setOwner(this);
240 if(dependencies==null)
241 dependencies = Collections.emptySet();
242 else {
243 // Until 1.207, we used to have ModuleName in dependencies. So convert.
244 Set<ModuleDependency> deps = new HashSet<ModuleDependency>(dependencies.size());
245 for (Object d : (Set<?>)dependencies) {
246 if (d instanceof ModuleDependency) {
247 deps.add((ModuleDependency) d);
248 } else {
249 deps.add(new ModuleDependency((ModuleName)d, ModuleDependency.UNKNOWN, false));
250 }
251 }
252 dependencies = deps;
253 }
254 }
255
256 /**
257 * Relative path to this module's root directory
258 * from the workspace of a {@link MavenModuleSet}.
259 *
260 * The path separator is normalized to '/'.
261 */
262 public String getRelativePath() {
263 return relativePath;
264 }
265
266 /**
267 * Gets the version number in Maven POM as of the last build.
268 *
269 * @return
270 * This method can return null if Jenkins loaded old data
271 * that didn't record this information, so that situation
272 * needs to be handled gracefully.
273 * @since 1.199
274 */
275 public String getVersion() {
276 return version;
277 }
278
279 /**
280 * Gets the list of goals to execute for this module.
281 */
282 public String getGoals() {
283 if(goals!=null) return goals;
284 return getParent().getGoals();
285 }
286
287 /**
288 * Gets the list of goals specified by the user,
289 * without taking inheritance and POM default goals
290 * into account.
291 *
292 * <p>
293 * This is only used to present the UI screen, and in
294 * all the other cases {@link #getGoals()} should be used.
295 */
296 public String getUserConfiguredGoals() {
297 return goals;
298 }
299
300 public DescribableList<Publisher,Descriptor<Publisher>> getPublishersList() {
301 // TODO
302 return new DescribableList<Publisher,Descriptor<Publisher>>(this);
303 }
304
305 @Override
306 public JDK getJDK() {
307 // share one setting for the whole module set.
308 return getParent().getJDK();
309 }
310
311 @Override
312 protected Class<MavenBuild> getBuildClass() {
313 return MavenBuild.class;
314 }
315
316 @Override
317 protected MavenBuild newBuild() throws IOException {
318 return super.newBuild();
319 }
320
321 public ModuleName getModuleName() {
322 return moduleName;
323 }
324
325 /**
326 * Gets groupId+artifactId+version as {@link ModuleDependency}.
327 */
328 public ModuleDependency asDependency() {
329 return new ModuleDependency(moduleName,Functions.defaulted(version,ModuleDependency.UNKNOWN),
330 PomInfo.PACKAGING_TYPE_PLUGIN.equals(this.packaging));
331 }
332
333 @Override
334 public String getShortUrl() {
335 return moduleName.toFileSystemName()+'/';
336 }
337
338 @Exported(visibility=2)
339 @Override
340 public String getDisplayName() {
341 return displayName;
342 }
343
344 @Override
345 public String getPronoun() {
346 return AlternativeUiTextProvider.get(PRONOUN, this, Messages.MavenModule_Pronoun());
347 }
348
349 @Override
350 public boolean isNameEditable() {
351 return false;
352 }
353
354 @Override
355 public MavenModuleSet getParent() {
356 return (MavenModuleSet)super.getParent();
357 }
358
359 /**
360 * Gets all the child modules (that are listed in the &lt;module> element in our POM.)
361 * <p>
362 * This method returns null if this information is not recorded. This happens
363 * for compatibility reason.
364 *
365 * @since 1.133
366 */
367 public List<MavenModule> getChildren() {
368 List<ModuleName> l = children; // take a snapshot
369 if(l==null) return null;
370
371 List<MavenModule> modules = new ArrayList<MavenModule>(l.size());
372 for (ModuleName n : l) {
373 MavenModule m = getParent().modules.get(n);
374 if(m!=null)
375 modules.add(m);
376 }
377 return modules;
378 }
379
380 /**
381 * {@link MavenModule} uses the workspace of the {@link MavenModuleSet},
382 * so it always needs to be built on the same slave as the parent.
383 */
384 @Override
385 public Label getAssignedLabel() {
386 Node n = getParent().getLastBuiltOn();
387 if(n==null) return null;
388 return n.getSelfLabel();
389 }
390
391 /**
392 * Workspace of a {@link MavenModule} is a part of the parent's workspace.
393 * <p>
394 * That is, {@Link MavenModuleSet} builds are incompatible with any {@link MavenModule}
395 * builds, whereas {@link MavenModule} builds are compatible with each other.
396 *
397 * @deprecated as of 1.319 in {@link AbstractProject}.
398 */
399 @Override
400 public Resource getWorkspaceResource() {
401 return new Resource(getParent().getWorkspaceResource(),getDisplayName()+" workspace");
402 }
403
404 @Override
405 public boolean isFingerprintConfigured() {
406 return true;
407 }
408
409 @Override // to make this accessible to MavenModuleSet
410 protected void updateTransientActions() {
411 super.updateTransientActions();
412 }
413
414 protected void buildDependencyGraph(DependencyGraph graph) {
415 if(!isBuildable() || getParent().ignoreUpstremChanges()) return;
416
417 MavenDependencyComputationData data = graph.getComputationalData(MavenDependencyComputationData.class);
418
419 // Build a map of all Maven modules in this Jenkins instance as dependencies.
420
421 // When we load old data that doesn't record version in dependency, we'd like
422 // to emulate the old behavior that tries to identify the upstream by ignoring the version.
423 // Do this by putting groupId:artifactId:UNKNOWN to the modules list, but
424 // ONLY if we find a such an old MavenModule in this Jenkins instance.
425 final boolean hasDependenciesWithUnknownVersion = hasDependenciesWithUnknownVersion();
426 if (data == null) {
427 Map<ModuleDependency,MavenModule> modules = new HashMap<ModuleDependency,MavenModule>();
428
429 for (MavenModule m : getAllMavenModules()) {
430 if(!m.isBuildable()) continue;
431 ModuleDependency moduleDependency = m.asDependency();
432 MavenModule old = modules.get(moduleDependency);
433 MavenModule relevant = chooseMoreRelevantModule(old, m);
434 modules.put(moduleDependency, relevant);
435 if (hasDependenciesWithUnknownVersion) {
436 modules.put(moduleDependency.withUnknownVersion(),relevant);
437 }
438 }
439 data = new MavenDependencyComputationData(modules);
440 data.withUnknownVersions = hasDependenciesWithUnknownVersion;
441 graph.putComputationalData(MavenDependencyComputationData.class, data);
442 } else {
443 if (hasDependenciesWithUnknownVersion && !data.withUnknownVersions) {
444 // found 'old' MavenModule: add dependencies with unknown versions now
445 for (MavenModule m : getAllMavenModules()) {
446 if(m.isDisabled()) continue;
447 ModuleDependency moduleDependency = m.asDependency().withUnknownVersion();
448 data.allModules.put(moduleDependency,m);
449 }
450 data.withUnknownVersions = true;
451 }
452 }
453
454 // Can lead to OOME, if remembered in the computational data and there are lot big multi-module projects
455 // TODO: try to use soft references to clean the heap when needed
456 Map<ModuleDependency,MavenModule> myParentsModules; // = data.modulesPerParent.get(getParent());
457
458 //if (myParentsModules == null) {
459 myParentsModules = new HashMap<ModuleDependency, MavenModule>();
460
461 for (MavenModule m : getParent().getModules()) {
462 if(m.isDisabled()) continue;
463 ModuleDependency moduleDependency = m.asDependency();
464 myParentsModules.put(moduleDependency,m);
465 if (hasDependenciesWithUnknownVersion) {
466 myParentsModules.put(moduleDependency.withUnknownVersion(),m);
467 }
468 }
469
470 //data.modulesPerParent.put(getParent(), myParentsModules);
471 //}
472
473 //Create a map of groupId:artifact id keys to modules for faster look ups in findMatchingDependentModule
474 Multimap<ModuleName,ModuleDependency> mapModules = data.byName();
475
476 for (ModuleDependency d : dependencies) {
477 MavenModule src;
478
479 // In case two modules with the same name are defined, modules in the same MavenModuleSet
480 // take precedence.
481 src = myParentsModules.get(d);
482
483 // otherwise we can pick the module with the highest version number (within the constraint that
484 // it satisfies 'd')
485 if (src==null) {
486 Collection<ModuleDependency> candidates = mapModules.get(d.getName());
487 ModuleDependency winner = d.findHighestFrom(candidates);
488 src = data.allModules.get(winner);
489 }
490
491 if(src!=null && !src.getParent().isDisableTriggerDownstreamProjects()) {
492 DependencyGraph.Dependency dep = new MavenModuleDependency(nodeOf(src),nodeOf(this));
493 if (!dep.pointsItself())
494 graph.addDependency(dep);
495 }
496 }
497 }
498
499 /**
500 * Determines the source/sink of the dependency from a module.
501 * This is because if the build is the aggregator build, we need to define dependencies against project,
502 * not module.
503 */
504 private static AbstractMavenProject<?, ?> nodeOf(MavenModule m) {
505 return m.getParent().isAggregatorStyleBuild() ? m.getParent() : m;
506 }
507
508 /**
509 * Returns all Maven modules in this Jenkins instance.
510 */
511 protected Collection<MavenModule> getAllMavenModules() {
512 return Jenkins.getInstance().getAllItems(MavenModule.class);
513 }
514
515 /**
516 * Check if this module has dependencies recorded without a concrete version -
517 * which shouldn't happen for any module which was at least build once with Jenkins >= 1.207.
518 */
519 private boolean hasDependenciesWithUnknownVersion() {
520 for (ModuleDependency dep : dependencies) {
521 if (ModuleDependency.UNKNOWN.equals(dep.version)) {
522 return true;
523 }
524 }
525 return false;
526 }
527
528 private MavenModule chooseMoreRelevantModule(MavenModule mm1, MavenModule mm2) {
529
530 if (mm1 == null) {
531 return mm2;
532 }
533 if (mm2 == null) {
534 return mm1;
535 }
536
537 int score = mm1.getDependencyRelevancy() - mm2.getDependencyRelevancy();
538 if (score==0) {
539 // tie breaker. this is arbitrary, but reproduceable
540 score = mm1.getParent().getFullName().compareTo(mm2.getParent().getFullName());
541 }
542 assert score!=0;
543
544 final MavenModule moreRelevant, lessRelevant;
545
546 if (score>0) { moreRelevant = mm1; lessRelevant = mm2; }
547 else { moreRelevant = mm2; lessRelevant = mm1; }
548
549 if (LOGGER.isLoggable(Level.FINER)) {
550 LOGGER.finer("Choosing " + moreRelevant.getParent().getName() + " over " + lessRelevant.getParent().getName()
551 + " for module " + mm1.asDependency().getName() + ". Relevancies: " + mm1.getDependencyRelevancy() + ", " + mm2.getDependencyRelevancy());
552 }
553 return moreRelevant;
554 }
555
556 /**
557 * As a guide for automatic dependency computation,
558 * determine how much this particular build is "relevant" to other builds on this Jenkins.
559 *
560 * If the binary is being deployed, we assume the user intends the result of this build be used elsewhere,
561 * so we give a higher score.
562 */
563 private int getDependencyRelevancy() {
564 int relevancy = 0;
565
566 for (String goal : Util.tokenize(getGoals())) {
567 if ("deploy".equals(goal) || "deploy:deploy".equals(goal)) {
568 return 2;
569 }
570
571 if ("install".equals(goal)) {
572 relevancy = 1;
573 }
574 }
575
576 for (Publisher publisher : getParent().getPublishers()) {
577 if (publisher instanceof RedeployPublisher) {
578 return 2;
579 }
580 }
581
582 return relevancy;
583 }
584
585 private static class MavenDependencyComputationData {
586 boolean withUnknownVersions = false;
587 /**
588 * All {@link MavenModule}s in this Jenkins, keyed by their {@link MavenModule#asDependency()}.
589 */
590 private final Map<ModuleDependency,MavenModule> allModules;
591
592 //Map<MavenModuleSet, Map<ModuleDependency,MavenModule>> modulesPerParent = new HashMap<MavenModuleSet, Map<ModuleDependency,MavenModule>>();
593
594 public MavenDependencyComputationData(Map<ModuleDependency, MavenModule> modules) {
595 this.allModules = modules;
596 }
597
598 /**
599 * Builds a map of all the modules, keyed against the groupId and artifactId. The values are a list of modules
600 * that match this criteria.
601 *
602 * @return {@link #allModules} keyed by their {@linkplain ModuleName names}.
603 */
604 private Multimap<ModuleName,ModuleDependency> byName() {
605 Multimap<ModuleName,ModuleDependency> map = HashMultimap.create();
606
607 for (ModuleDependency dependency : allModules.keySet()) {
608 map.put(dependency.getName(),dependency);
609 }
610
611 return map;
612 }
613 }
614
615 @Override
616 protected void addTransientActionsFromBuild(MavenBuild build, List<Action> collection, Set<Class> added) {
617 if(build==null) return;
618 List<MavenProjectActionBuilder> list = build.projectActionReporters;
619 if(list==null) return;
620
621 for (MavenProjectActionBuilder step : list) {
622 if(!added.add(step.getClass())) continue; // already added
623 try {
624 collection.addAll(step.getProjectActions(this));
625 } catch (Exception e) {
626 LOGGER.log(Level.WARNING, "Failed to getProjectAction from " + step
627 + ". Report issue to plugin developers.", e);
628 }
629 }
630 }
631
632 public MavenInstallation inferMavenInstallation() {
633 return getParent().inferMavenInstallation();
634 }
635
636 /**
637 * List of active {@link MavenReporter}s configured for this module.
638 */
639 public DescribableList<MavenReporter, Descriptor<MavenReporter>> getReporters() {
640 return reporters;
641 }
642
643 @Override
644 protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
645 super.submit(req, rsp);
646
647 reporters.rebuild(req, req.getSubmittedForm(),MavenReporters.getConfigurableList());
648
649 goals = Util.fixEmpty(req.getParameter("goals").trim());
650
651 // dependency setting might have been changed by the user, so rebuild.
652 Jenkins.getInstance().rebuildDependencyGraph();
653 }
654
655 @Override
656 protected void performDelete() throws IOException, InterruptedException {
657 super.performDelete();
658 getParent().onModuleDeleted(this);
659 }
660
661 /**
662 * Creates a list of {@link MavenReporter}s to be used for a build of this project.
663 */
664 protected List<MavenReporter> createReporters() {
665 List<MavenReporter> reporterList = new ArrayList<MavenReporter>();
666
667 getReporters().addAllTo(reporterList);
668 getParent().getReporters().addAllTo(reporterList);
669
670 for (MavenReporterDescriptor d : MavenReporterDescriptor.all()) {
671 if(getReporters().contains(d))
672 continue; // already configured
673 MavenReporter auto = d.newAutoInstance(this);
674 if(auto!=null)
675 reporterList.add(auto);
676 }
677
678 return reporterList;
679 }
680
681 /**
682 * for debug purpose
683 */
684 public String toString() {
685 return super.toString()+'['+getFullName()+']'+"[relativePath:"+getRelativePath()+']';
686 }
687
688 private static final Logger LOGGER = Logger.getLogger(MavenModule.class.getName());
689
690 }
691