001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.enforcer.rules; 020 021import javax.inject.Inject; 022import javax.inject.Named; 023 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.HashSet; 030import java.util.List; 031import java.util.Map; 032import java.util.Objects; 033import java.util.Set; 034 035import org.apache.maven.BuildFailureException; 036import org.apache.maven.RepositoryUtils; 037import org.apache.maven.artifact.Artifact; 038import org.apache.maven.artifact.factory.ArtifactFactory; 039import org.apache.maven.artifact.repository.ArtifactRepository; 040import org.apache.maven.artifact.resolver.ArtifactNotFoundException; 041import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; 042import org.apache.maven.artifact.versioning.VersionRange; 043import org.apache.maven.enforcer.rule.api.EnforcerRuleError; 044import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 045import org.apache.maven.enforcer.rules.utils.EnforcerRuleUtils; 046import org.apache.maven.enforcer.rules.utils.ExpressionEvaluator; 047import org.apache.maven.enforcer.rules.utils.PluginWrapper; 048import org.apache.maven.execution.MavenSession; 049import org.apache.maven.lifecycle.DefaultLifecycles; 050import org.apache.maven.lifecycle.Lifecycle; 051import org.apache.maven.lifecycle.LifecycleExecutionException; 052import org.apache.maven.lifecycle.mapping.LifecycleMapping; 053import org.apache.maven.model.BuildBase; 054import org.apache.maven.model.Model; 055import org.apache.maven.model.ModelBase; 056import org.apache.maven.model.Plugin; 057import org.apache.maven.model.PluginConfiguration; 058import org.apache.maven.model.PluginContainer; 059import org.apache.maven.model.Profile; 060import org.apache.maven.model.ReportPlugin; 061import org.apache.maven.model.Reporting; 062import org.apache.maven.plugin.InvalidPluginException; 063import org.apache.maven.plugin.PluginManager; 064import org.apache.maven.plugin.PluginManagerException; 065import org.apache.maven.plugin.PluginNotFoundException; 066import org.apache.maven.plugin.version.PluginVersionNotFoundException; 067import org.apache.maven.plugin.version.PluginVersionResolutionException; 068import org.apache.maven.project.MavenProject; 069import org.apache.maven.rtinfo.RuntimeInformation; 070import org.apache.maven.settings.Settings; 071import org.codehaus.plexus.PlexusContainer; 072import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 073import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 074import org.codehaus.plexus.util.StringUtils; 075import org.eclipse.aether.RepositorySystem; 076import org.eclipse.aether.resolution.ArtifactRequest; 077import org.eclipse.aether.resolution.ArtifactResolutionException; 078 079import static java.util.Optional.ofNullable; 080 081/** 082 * This rule will enforce that all plugins specified in the poms have a version declared. 083 * 084 * @author <a href="mailto:brianf@apache.org">Brian Fox</a> 085 */ 086@Named("requirePluginVersions") 087public final class RequirePluginVersions extends AbstractStandardEnforcerRule { 088 089 /** 090 * Don't allow the LATEST identifier. 091 */ 092 private boolean banLatest = true; 093 094 /** 095 * Don't allow the RELEASE identifier. 096 */ 097 private boolean banRelease = true; 098 099 /** 100 * Don't allow snapshot plugins. 101 */ 102 private boolean banSnapshots = true; 103 104 /** 105 * Don't allow timestamp snapshot plugins. 106 */ 107 private boolean banTimestamps = true; 108 109 /** 110 * @since 3.0.0 111 */ 112 private boolean banMavenDefaults = true; 113 114 /** 115 * The comma separated list of phases that should be used to find lifecycle plugin bindings. The default value is 116 * "clean,deploy,site". 117 */ 118 private String phases = "clean,deploy,site"; 119 120 /** 121 * Additional plugins to enforce have versions. These are plugins that may not be in the poms but are used anyway, 122 * like help, eclipse etc. <br> 123 * The plugins should be specified in the form: <code>group:artifactId</code>. 124 */ 125 private List<String> additionalPlugins; 126 127 /** 128 * Plugins to skip for version enforcement. The plugins should be specified in the form: 129 * <code>group:artifactId</code>. NOTE: This is deprecated, use unCheckedPluginList instead. 130 */ 131 private List<String> unCheckedPlugins; 132 133 /** 134 * Same as unCheckedPlugins but as a comma list to better support properties. Sample form: 135 * <code>group:artifactId,group2:artifactId2</code> 136 * 137 * @since 1.0-beta-1 138 */ 139 private String unCheckedPluginList; 140 141 /** The phase to lifecycle map. */ 142 private Map<String, Lifecycle> phaseToLifecycleMap; 143 144 /** The lifecycles. */ 145 private Collection<Lifecycle> lifecycles; 146 147 /** The plugin manager. */ 148 private final PluginManager pluginManager; 149 150 /** The factory. */ 151 private final ArtifactFactory factory; 152 153 private final RepositorySystem repositorySystem; 154 155 /** The session. */ 156 private final MavenSession session; 157 158 /** The utils. */ 159 private final EnforcerRuleUtils utils; 160 161 private final RuntimeInformation runtimeInformation; 162 163 private final DefaultLifecycles defaultLifeCycles; 164 165 private final MavenProject project; 166 167 private final ExpressionEvaluator evaluator; 168 169 private final PlexusContainer container; 170 171 @SuppressWarnings("checkstyle:ParameterNumber") 172 @Inject 173 public RequirePluginVersions( 174 PluginManager pluginManager, 175 ArtifactFactory factory, 176 RepositorySystem repositorySystem, 177 MavenSession session, 178 EnforcerRuleUtils utils, 179 RuntimeInformation runtimeInformation, 180 DefaultLifecycles defaultLifeCycles, 181 MavenProject project, 182 ExpressionEvaluator evaluator, 183 PlexusContainer container) { 184 this.pluginManager = Objects.requireNonNull(pluginManager); 185 this.factory = Objects.requireNonNull(factory); 186 this.repositorySystem = Objects.requireNonNull(repositorySystem); 187 this.session = Objects.requireNonNull(session); 188 this.utils = Objects.requireNonNull(utils); 189 this.runtimeInformation = Objects.requireNonNull(runtimeInformation); 190 this.defaultLifeCycles = Objects.requireNonNull(defaultLifeCycles); 191 this.project = Objects.requireNonNull(project); 192 this.evaluator = Objects.requireNonNull(evaluator); 193 this.container = Objects.requireNonNull(container); 194 } 195 196 @Override 197 public void execute() throws EnforcerRuleException { 198 199 try { 200 // get the various expressions out of the helper. 201 202 lifecycles = defaultLifeCycles.getLifeCycles(); 203 204 // get all the plugins that are bound to the specified lifecycles 205 Set<Plugin> allPlugins = getBoundPlugins(project, phases); 206 207 // insert any additional plugins specified by the user. 208 allPlugins = addAdditionalPlugins(allPlugins, additionalPlugins); 209 allPlugins.addAll(getProfilePlugins(project)); 210 211 // pull out any we should skip 212 allPlugins = 213 removeUncheckedPlugins(combineUncheckedPlugins(unCheckedPlugins, unCheckedPluginList), allPlugins); 214 215 // there's nothing to do here 216 if (allPlugins.isEmpty()) { 217 getLog().info("No plugin bindings found."); 218 return; 219 } else { 220 getLog().debug("All Plugins in use: " + allPlugins); 221 } 222 223 // get all the plugins that are mentioned in the pom (and parents) 224 List<PluginWrapper> pluginWrappers = getAllPluginEntries(project); 225 226 for (PluginWrapper pluginWrapper : pluginWrappers) { 227 getLog().debug("pluginWrappers: " + pluginWrapper.getGroupId() + ":" + pluginWrapper.getArtifactId() 228 + ":" + pluginWrapper.getVersion() + " source: " + pluginWrapper.getSource()); 229 } 230 // now look for the versions that aren't valid and add to a list. 231 List<Plugin> failures = new ArrayList<>(); 232 233 for (Plugin plugin : allPlugins) { 234 if (!hasValidVersionSpecified(plugin, pluginWrappers)) { 235 failures.add(plugin); 236 } 237 } 238 239 // if anything was found, log it then append the optional message. 240 if (!failures.isEmpty()) { 241 handleMessagesToTheUser(project, failures); 242 } 243 } catch (PluginNotFoundException | LifecycleExecutionException e) { 244 throw new EnforcerRuleException(e.getLocalizedMessage(), e); 245 } 246 } 247 248 private void handleMessagesToTheUser(MavenProject project, List<Plugin> failures) throws EnforcerRuleException { 249 StringBuilder newMsg = new StringBuilder(); 250 newMsg.append("Some plugins are missing valid versions or depend on Maven "); 251 newMsg.append(runtimeInformation.getMavenVersion()); 252 newMsg.append(" defaults"); 253 handleBanMessages(newMsg); 254 newMsg.append(System.lineSeparator()); 255 for (Plugin plugin : failures) { 256 newMsg.append(" "); 257 newMsg.append(plugin.getGroupId()); 258 newMsg.append(":"); 259 newMsg.append(plugin.getArtifactId()); 260 261 try { 262 newMsg.append(". \tThe version currently in use is "); 263 264 Plugin currentPlugin = findCurrentPlugin(plugin, project); 265 266 if (currentPlugin == null) { 267 newMsg.append("unknown"); 268 } else { 269 newMsg.append(currentPlugin.getVersion()); 270 271 if (PluginWrapper.isVersionFromDefaultLifecycleBindings(currentPlugin) 272 .orElse(false)) { 273 newMsg.append(" via default lifecycle bindings"); 274 } else { 275 String msg = PluginWrapper.isVersionFromSuperpom(currentPlugin) 276 .filter(b -> b) 277 .map(t -> " via super POM") 278 // for Maven 3.6.0 or before (MNG-6593 / MNG-6600) 279 .orElse(" via super POM or default lifecycle bindings"); 280 newMsg.append(msg); 281 } 282 } 283 } catch (Exception e) { 284 // lots can go wrong here. Don't allow any issues trying to 285 // determine the issue stop me 286 getLog().debug("Exception while determining plugin Version " + e.getMessage()); 287 newMsg.append(". Unable to determine the plugin version."); 288 } 289 newMsg.append(System.lineSeparator()); 290 } 291 String message = getMessage(); 292 if (message != null && !message.isEmpty()) { 293 newMsg.append(message); 294 } 295 296 throw new EnforcerRuleException(newMsg.toString()); 297 } 298 299 private void handleBanMessages(StringBuilder newMsg) { 300 if (banLatest || banRelease || banSnapshots || banTimestamps) { 301 List<String> banList = new ArrayList<>(); 302 if (banLatest) { 303 banList.add("LATEST"); 304 } 305 if (banRelease) { 306 banList.add("RELEASE"); 307 } 308 if (banSnapshots) { 309 banList.add("SNAPSHOT"); 310 if (banTimestamps) { 311 banList.add("TIMESTAMP SNAPSHOT"); 312 } 313 } 314 if (!banList.isEmpty()) { 315 newMsg.append(" ("); 316 newMsg.append(String.join(", ", banList)); 317 newMsg.append(" as plugin version are not allowed)"); 318 } 319 } 320 } 321 322 /** 323 * Remove the plugins that the user doesn't want to check. 324 * 325 * @param uncheckedPlugins 326 * @param plugins 327 * @return The plugins which have been removed. 328 */ 329 Set<Plugin> removeUncheckedPlugins(Collection<String> uncheckedPlugins, Set<Plugin> plugins) 330 throws EnforcerRuleError { 331 if (uncheckedPlugins != null && !uncheckedPlugins.isEmpty()) { 332 for (String pluginKey : uncheckedPlugins) { 333 Plugin plugin = parsePluginString(pluginKey, "UncheckedPlugins"); 334 plugins.remove(plugin); 335 } 336 } 337 return plugins; 338 } 339 340 /** 341 * Combines the old Collection with the new comma separated list. 342 * 343 * @param uncheckedPlugins a new collections 344 * @param uncheckedPluginsList a list to merge 345 * @return List of unchecked plugins. 346 */ 347 public Collection<String> combineUncheckedPlugins( 348 Collection<String> uncheckedPlugins, String uncheckedPluginsList) { 349 // if the comma list is empty, then there's nothing to do here. 350 if (uncheckedPluginsList != null && !uncheckedPluginsList.isEmpty()) { 351 // make sure there is a collection to add to. 352 if (uncheckedPlugins == null) { 353 uncheckedPlugins = new HashSet<>(); 354 } else if (!uncheckedPlugins.isEmpty()) { 355 getLog().warn("The parameter 'unCheckedPlugins' is deprecated. Use 'unCheckedPluginList' instead"); 356 } 357 358 uncheckedPlugins.addAll(Arrays.asList(uncheckedPluginsList.split(","))); 359 } 360 return uncheckedPlugins; 361 } 362 363 /** 364 * Add the additional plugins if they don't exist yet. 365 * 366 * @param existing the existing plugins 367 * @param additional the additional plugins 368 * @return the additional and existing plugins 369 * @throws EnforcerRuleError the enforcer error 370 */ 371 public Set<Plugin> addAdditionalPlugins(Set<Plugin> existing, List<String> additional) throws EnforcerRuleError { 372 if (additional != null) { 373 if (existing == null) { 374 existing = new HashSet<>(); 375 } 376 for (String pluginString : additional) { 377 Plugin plugin = parsePluginString(pluginString, "AdditionalPlugins"); 378 existing.add(plugin); 379 } 380 } 381 return existing; 382 } 383 384 /** 385 * Helper method to parse and inject a Plugin. 386 * 387 * @param pluginString a plugin description to parse 388 * @param field a source of pluginString 389 * @return the prepared plugin 390 */ 391 private Plugin parsePluginString(String pluginString, String field) throws EnforcerRuleError { 392 if (pluginString != null) { 393 String[] pluginStrings = pluginString.split(":"); 394 if (pluginStrings.length == 2) { 395 Plugin plugin = new Plugin(); 396 plugin.setGroupId(StringUtils.strip(pluginStrings[0])); 397 plugin.setArtifactId(StringUtils.strip(pluginStrings[1])); 398 399 return plugin; 400 } else { 401 throw new EnforcerRuleError("Invalid " + field + " string: " + pluginString); 402 } 403 } else { 404 throw new EnforcerRuleError("Invalid " + field + " null plugin string."); 405 } 406 } 407 408 /** 409 * Finds the plugins that are listed in active profiles. 410 * 411 * @param project the project 412 * @return the profile plugins 413 */ 414 public Set<Plugin> getProfilePlugins(MavenProject project) { 415 Set<Plugin> result = new HashSet<>(); 416 List<Profile> profiles = project.getActiveProfiles(); 417 if (profiles != null && !profiles.isEmpty()) { 418 for (Profile p : profiles) { 419 BuildBase b = p.getBuild(); 420 if (b != null) { 421 List<Plugin> plugins = b.getPlugins(); 422 if (plugins != null) { 423 result.addAll(plugins); 424 } 425 } 426 } 427 } 428 return result; 429 } 430 431 /** 432 * Given a plugin, this will retrieve the matching plugin artifact from the model. 433 * 434 * @param plugin plugin to lookup 435 * @param project project to search 436 * @return matching plugin, <code>null</code> if not found. 437 */ 438 private Plugin findCurrentPlugin(Plugin plugin, MavenProject project) throws EnforcerRuleException { 439 Plugin found = null; 440 try { 441 Model model = project.getModel(); 442 Map<String, Plugin> plugins = model.getBuild().getPluginsAsMap(); 443 found = plugins.get(plugin.getKey()); 444 } catch (NullPointerException e) { 445 // nothing to do here 446 } 447 448 if (found == null) { 449 Artifact artifact = factory.createPluginArtifact( 450 plugin.getGroupId(), plugin.getArtifactId(), VersionRange.createFromVersion("LATEST")); 451 452 try { 453 repositorySystem.resolveArtifact( 454 session.getRepositorySession(), 455 new ArtifactRequest( 456 RepositoryUtils.toArtifact(artifact), 457 session.getCurrentProject().getRemotePluginRepositories(), 458 "resolvePlugin")); 459 } catch (ArtifactResolutionException e) { 460 throw new EnforcerRuleException("Unable to resolve the plugin " + artifact.getArtifactId(), e); 461 } 462 plugin.setVersion(artifact.getVersion()); 463 464 found = plugin; 465 } 466 467 return found; 468 } 469 470 /** 471 * Gets the plugins that are bound to the defined phases. This does not find plugins bound in the pom to a phase 472 * later than the plugin is executing. 473 * 474 * @param project the project 475 * @param phases the phases 476 * @return the bound plugins 477 * @throws PluginNotFoundException the plugin not found exception 478 * @throws LifecycleExecutionException the lifecycle execution exception 479 */ 480 private Set<Plugin> getBoundPlugins(MavenProject project, String phases) 481 throws PluginNotFoundException, LifecycleExecutionException { 482 483 Set<Plugin> allPlugins = new HashSet<>(); 484 485 // lookup the bindings for all the passed in phases 486 String[] lifecyclePhases = phases.split(","); 487 for (int i = 0; i < lifecyclePhases.length; i++) { 488 String lifecyclePhase = lifecyclePhases[i]; 489 if (lifecyclePhase != null && !lifecyclePhase.isEmpty()) { 490 try { 491 Lifecycle lifecycle = getLifecycleForPhase(lifecyclePhase); 492 getLog().debug("getBoundPlugins(): " + project.getId() + " " + lifecyclePhase + " " 493 + lifecycle.getId()); 494 allPlugins.addAll(getAllPlugins(project, lifecycle)); 495 } catch (BuildFailureException e) { 496 // swallow this because the 497 // user may have declared a phase that 498 // doesn't exist for every module. 499 } 500 } 501 } 502 return allPlugins; 503 } 504 505 /** 506 * Checks for valid version specified. Checks to see if the version is specified for the plugin. Can optionally ban 507 * "RELEASE" or "LATEST" even if specified. 508 * 509 * @param source the source 510 * @param pluginWrappers the plugins 511 * @return true, if successful 512 */ 513 public boolean hasValidVersionSpecified(Plugin source, List<PluginWrapper> pluginWrappers) { 514 boolean found = false; 515 boolean status = false; 516 for (PluginWrapper plugin : pluginWrappers) { 517 // find the matching plugin entry 518 if (isMatchingPlugin(source, plugin)) { 519 found = true; 520 // found the entry. now see if the version is specified 521 String version = plugin.getVersion(); 522 try { 523 version = (String) evaluator.evaluate(version); 524 } catch (ExpressionEvaluationException e) { 525 return false; 526 } 527 528 if (isValidVersion(version)) { 529 getLog().debug("checking for notEmpty and notIsWhitespace(): " + version); 530 if (banRelease && version.equals("RELEASE")) { 531 return false; 532 } 533 534 if (banLatest && version.equals("LATEST")) { 535 return false; 536 } 537 538 if (banSnapshots && isSnapshot(version)) { 539 return false; 540 } 541 // the version was specified and not 542 // banned. It's ok. Keep looking through the list to make 543 // sure it's not using a banned version somewhere else. 544 545 status = true; 546 547 if (!banRelease && !banLatest && !banSnapshots) { 548 // no need to keep looking 549 break; 550 } 551 } 552 } 553 } 554 if (!found) { 555 getLog().debug("plugin " + source.getGroupId() + ":" + source.getArtifactId() + " not found"); 556 } 557 return status; 558 } 559 560 private boolean isValidVersion(String version) { 561 return (version != null && !version.isEmpty()) && !StringUtils.isWhitespace(version); 562 } 563 564 private boolean isMatchingPlugin(Plugin source, PluginWrapper plugin) { 565 return source.getArtifactId().equals(plugin.getArtifactId()) 566 && source.getGroupId().equals(plugin.getGroupId()); 567 } 568 569 /** 570 * Checks if is snapshot. 571 * 572 * @param baseVersion the base version 573 * @return true, if is snapshot 574 */ 575 private boolean isSnapshot(String baseVersion) { 576 if (banTimestamps) { 577 return Artifact.VERSION_FILE_PATTERN.matcher(baseVersion).matches() 578 || baseVersion.endsWith(Artifact.SNAPSHOT_VERSION); 579 } else { 580 return baseVersion.endsWith(Artifact.SNAPSHOT_VERSION); 581 } 582 } 583 584 /* 585 * Uses borrowed lifecycle code to get a list of all plugins bound to the lifecycle. 586 */ 587 588 /** 589 * Gets the all plugins. 590 * 591 * @param project the project 592 * @param lifecycle the lifecycle 593 * @return the all plugins 594 * @throws PluginNotFoundException the plugin not found exception 595 * @throws LifecycleExecutionException the lifecycle execution exception 596 */ 597 private Set<Plugin> getAllPlugins(MavenProject project, Lifecycle lifecycle) 598 throws PluginNotFoundException, LifecycleExecutionException { 599 600 getLog().debug("RequirePluginVersions.getAllPlugins:"); 601 602 Set<Plugin> plugins = new HashSet<>(); 603 // first, bind those associated with the packaging 604 Map<String, String> mappings = findMappingsForLifecycle(project, lifecycle); 605 606 for (Map.Entry<String, String> entry : mappings.entrySet()) { 607 getLog().debug(" lifecycleMapping = " + entry.getKey()); 608 String pluginsForLifecycle = entry.getValue(); 609 getLog().debug(" plugins = " + pluginsForLifecycle); 610 if (pluginsForLifecycle != null && !pluginsForLifecycle.isEmpty()) { 611 String pluginList[] = pluginsForLifecycle.split(","); 612 for (String plugin : pluginList) { 613 plugin = StringUtils.strip(plugin); 614 getLog().debug(" plugin = " + plugin); 615 String tokens[] = plugin.split(":"); 616 getLog().debug(" GAV = " + Arrays.asList(tokens)); 617 618 Plugin p = new Plugin(); 619 p.setGroupId(tokens[0]); 620 p.setArtifactId(tokens[1]); 621 plugins.add(p); 622 } 623 } 624 } 625 626 plugins.addAll(project.getBuildPlugins()); 627 628 return plugins; 629 } 630 631 /* 632 * NOTE: All the code following this point was scooped from the DefaultLifecycleExecutor. There must be a better way 633 * but for now it should work. 634 */ 635 636 /** 637 * Gets the phase to lifecycle map. 638 * 639 * @return the phase to lifecycle map 640 * @throws LifecycleExecutionException the lifecycle execution exception 641 */ 642 public Map<String, Lifecycle> getPhaseToLifecycleMap() throws LifecycleExecutionException { 643 if (phaseToLifecycleMap == null) { 644 phaseToLifecycleMap = new HashMap<>(); 645 646 for (Lifecycle lifecycle : lifecycles) { 647 List<String> phases = lifecycle.getPhases(); 648 for (String phase : phases) { 649 getLog().debug("getPhaseToLifecycleMap(): phase: " + phase); 650 if (phaseToLifecycleMap.containsKey(phase)) { 651 Lifecycle prevLifecycle = phaseToLifecycleMap.get(phase); 652 throw new LifecycleExecutionException("Phase '" + phase 653 + "' is defined in more than one lifecycle: '" + lifecycle.getId() + "' and '" 654 + prevLifecycle.getId() + "'"); 655 } else { 656 phaseToLifecycleMap.put(phase, lifecycle); 657 } 658 } 659 } 660 } 661 return phaseToLifecycleMap; 662 } 663 664 /** 665 * Gets the lifecycle for phase. 666 * 667 * @param phase the phase 668 * @return the lifecycle for phase 669 * @throws BuildFailureException the build failure exception 670 * @throws LifecycleExecutionException the lifecycle execution exception 671 */ 672 private Lifecycle getLifecycleForPhase(String phase) throws BuildFailureException, LifecycleExecutionException { 673 Lifecycle lifecycle = getPhaseToLifecycleMap().get(phase); 674 675 if (lifecycle == null) { 676 throw new BuildFailureException("Unable to find lifecycle for phase '" + phase + "'"); 677 } 678 return lifecycle; 679 } 680 681 /** 682 * Find mappings for lifecycle. 683 * 684 * @param project the project 685 * @param lifecycle the lifecycle 686 * @return the map 687 * @throws LifecycleExecutionException the lifecycle execution exception 688 * @throws PluginNotFoundException the plugin not found exception 689 */ 690 private Map<String, String> findMappingsForLifecycle(MavenProject project, Lifecycle lifecycle) 691 throws LifecycleExecutionException, PluginNotFoundException { 692 String packaging = project.getPackaging(); 693 Map<String, String> mappings = null; 694 695 LifecycleMapping m = (LifecycleMapping) findExtension( 696 project, LifecycleMapping.ROLE, packaging, session.getSettings(), session.getLocalRepository()); 697 if (m != null) { 698 mappings = m.getPhases(lifecycle.getId()); 699 } 700 701 Map<String, String> defaultMappings = lifecycle.getDefaultPhases(); 702 703 if (mappings == null) { 704 try { 705 m = container.lookup(LifecycleMapping.class, packaging); 706 mappings = m.getPhases(lifecycle.getId()); 707 } catch (ComponentLookupException e) { 708 if (defaultMappings == null) { 709 throw new LifecycleExecutionException( 710 "Cannot find lifecycle mapping for packaging: '" + packaging + "'.", e); 711 } 712 } 713 } 714 715 if (mappings == null) { 716 if (defaultMappings == null) { 717 throw new LifecycleExecutionException( 718 "Cannot find lifecycle mapping for packaging: '" + packaging + "', and there is no default"); 719 } else { 720 mappings = defaultMappings; 721 } 722 } 723 724 return mappings; 725 } 726 727 /** 728 * Find extension. 729 * 730 * @param project the project 731 * @param role the role 732 * @param roleHint the role hint 733 * @param settings the settings 734 * @param localRepository the local repository 735 * @return the object 736 * @throws LifecycleExecutionException the lifecycle execution exception 737 * @throws PluginNotFoundException the plugin not found exception 738 */ 739 private Object findExtension( 740 MavenProject project, String role, String roleHint, Settings settings, ArtifactRepository localRepository) 741 throws LifecycleExecutionException, PluginNotFoundException { 742 Object pluginComponent = null; 743 744 List<Plugin> buildPlugins = project.getBuildPlugins(); 745 for (Plugin plugin : buildPlugins) { 746 if (plugin.isExtensions()) { 747 verifyPlugin(plugin, project, settings, localRepository); 748 749 // TODO: if moved to the plugin manager we 750 // already have the descriptor from above 751 // and so do can lookup the container 752 // directly 753 try { 754 pluginComponent = pluginManager.getPluginComponent(plugin, role, roleHint); 755 756 if (pluginComponent != null) { 757 break; 758 } 759 } catch (ComponentLookupException e) { 760 getLog().debug("Unable to find the lifecycle component in the extension " + e.getMessage()); 761 } catch (PluginManagerException e) { 762 throw new LifecycleExecutionException( 763 "Error getting extensions from the plugin '" + plugin.getKey() + "': " + e.getMessage(), e); 764 } 765 } 766 } 767 return pluginComponent; 768 } 769 770 /** 771 * Verify plugin. 772 * 773 * @param plugin the plugin 774 * @param project the project 775 * @param settings the settings 776 * @param localRepository the local repository 777 * @return the plugin descriptor 778 * @throws LifecycleExecutionException the lifecycle execution exception 779 * @throws PluginNotFoundException the plugin not found exception 780 */ 781 private void verifyPlugin( 782 Plugin plugin, MavenProject project, Settings settings, ArtifactRepository localRepository) 783 throws LifecycleExecutionException, PluginNotFoundException { 784 try { 785 pluginManager.verifyPlugin(plugin, project, settings, localRepository); 786 } catch (PluginManagerException e) { 787 throw new LifecycleExecutionException( 788 "Internal error in the plugin manager getting plugin '" + plugin.getKey() + "': " + e.getMessage(), 789 e); 790 } catch (PluginVersionResolutionException 791 | InvalidVersionSpecificationException 792 | InvalidPluginException 793 | PluginVersionNotFoundException 794 | org.apache.maven.artifact.resolver.ArtifactResolutionException 795 | ArtifactNotFoundException e) { 796 throw new LifecycleExecutionException(e.getMessage(), e); 797 } 798 } 799 800 /** 801 * Gets all plugin entries in build.plugins, build.pluginManagement.plugins, profile.build.plugins, reporting and 802 * profile.reporting in this project and all parents 803 * 804 * @param project the project 805 * @return the all plugin entries wrapped in a PluginWrapper Object 806 */ 807 private List<PluginWrapper> getAllPluginEntries(MavenProject project) { 808 List<PluginWrapper> plugins = new ArrayList<>(); 809 // now find all the plugin entries, either in 810 // build.plugins or build.pluginManagement.plugins, profiles.plugins and reporting 811 812 getPlugins(plugins, project.getModel()); 813 getReportingPlugins(plugins, project.getModel()); 814 getPluginManagementPlugins(plugins, project.getModel()); 815 addPluginsInProfiles(plugins, project.getModel()); 816 817 return plugins; 818 } 819 820 private void addPluginsInProfiles(List<PluginWrapper> plugins, Model model) { 821 List<Profile> profiles = ofNullable(model).map(Model::getProfiles).orElseGet(Collections::emptyList); 822 for (Profile profile : profiles) { 823 getProfilePlugins(plugins, profile); 824 getProfileReportingPlugins(plugins, profile); 825 getProfilePluginManagementPlugins(plugins, profile); 826 } 827 } 828 829 private void getProfilePluginManagementPlugins(List<PluginWrapper> plugins, Profile profile) { 830 List<Plugin> modelPlugins = ofNullable(profile) 831 .map(Profile::getBuild) 832 .map(PluginConfiguration::getPluginManagement) 833 .map(PluginContainer::getPlugins) 834 .orElseGet(Collections::emptyList); 835 plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults)); 836 } 837 838 private void getProfileReportingPlugins(List<PluginWrapper> plugins, Profile profile) { 839 List<ReportPlugin> modelReportPlugins = ofNullable(profile) 840 .map(ModelBase::getReporting) 841 .map(Reporting::getPlugins) 842 .orElseGet(Collections::emptyList); 843 // add the reporting plugins 844 plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults)); 845 } 846 847 private void getProfilePlugins(List<PluginWrapper> plugins, Profile profile) { 848 List<Plugin> modelPlugins = ofNullable(profile) 849 .map(Profile::getBuild) 850 .map(PluginContainer::getPlugins) 851 .orElseGet(Collections::emptyList); 852 plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults)); 853 } 854 855 private void getPlugins(List<PluginWrapper> plugins, Model model) { 856 List<Plugin> modelPlugins = ofNullable(model) 857 .map(Model::getBuild) 858 .map(PluginContainer::getPlugins) 859 .orElseGet(Collections::emptyList); 860 plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults)); 861 } 862 863 private void getPluginManagementPlugins(List<PluginWrapper> plugins, Model model) { 864 List<Plugin> modelPlugins = ofNullable(model) 865 .map(Model::getBuild) 866 .map(PluginConfiguration::getPluginManagement) 867 .map(PluginContainer::getPlugins) 868 .orElseGet(Collections::emptyList); 869 plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults)); 870 } 871 872 private void getReportingPlugins(List<PluginWrapper> plugins, Model model) { 873 List<ReportPlugin> modelReportPlugins = ofNullable(model) 874 .map(ModelBase::getReporting) 875 .map(Reporting::getPlugins) 876 .orElseGet(Collections::emptyList); 877 // add the reporting plugins 878 plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults)); 879 } 880 881 /** 882 * Sets the ban latest. 883 * 884 * @param banLatest the banLatest to set 885 */ 886 public void setBanLatest(boolean banLatest) { 887 this.banLatest = banLatest; 888 } 889 890 /** 891 * Sets the ban release. 892 * 893 * @param banRelease the banRelease to set 894 */ 895 public void setBanRelease(boolean banRelease) { 896 this.banRelease = banRelease; 897 } 898 899 /** 900 * Checks if is ban snapshots. 901 * 902 * @return the banSnapshots 903 */ 904 public boolean isBanSnapshots() { 905 return this.banSnapshots; 906 } 907 908 /** 909 * Sets the ban snapshots. 910 * 911 * @param banSnapshots the banSnapshots to set 912 */ 913 public void setBanSnapshots(boolean banSnapshots) { 914 this.banSnapshots = banSnapshots; 915 } 916 917 /** 918 * Sets the ban timestamps. 919 * 920 * @param banTimestamps the banTimestamps to set 921 */ 922 public void setBanTimestamps(boolean banTimestamps) { 923 this.banTimestamps = banTimestamps; 924 } 925 926 @Override 927 public String toString() { 928 return String.format( 929 "RequirePluginVersions[message=%s, banLatest=%b, banRelease=%b, banSnapshots=%b, banTimestamps=%b, phases=%s, additionalPlugins=%s, unCheckedPluginList=%s, unCheckedPlugins=%s]", 930 getMessage(), 931 banLatest, 932 banRelease, 933 banSnapshots, 934 banTimestamps, 935 phases, 936 additionalPlugins, 937 unCheckedPluginList, 938 unCheckedPlugins); 939 } 940}