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.descriptor.PluginDescriptor; 067import org.apache.maven.plugin.version.PluginVersionNotFoundException; 068import org.apache.maven.plugin.version.PluginVersionResolutionException; 069import org.apache.maven.project.MavenProject; 070import org.apache.maven.rtinfo.RuntimeInformation; 071import org.apache.maven.settings.Settings; 072import org.codehaus.plexus.PlexusContainer; 073import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 074import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 075import org.codehaus.plexus.util.StringUtils; 076import org.eclipse.aether.RepositorySystem; 077import org.eclipse.aether.resolution.ArtifactRequest; 078import org.eclipse.aether.resolution.ArtifactResolutionException; 079 080import static java.util.Optional.ofNullable; 081 082/** 083 * This rule will enforce that all plugins specified in the poms have a version declared. 084 * 085 * @author <a href="mailto:brianf@apache.org">Brian Fox</a> 086 */ 087@Named("requirePluginVersions") 088public final class RequirePluginVersions extends AbstractStandardEnforcerRule { 089 090 /** 091 * Don't allow the LATEST identifier. 092 */ 093 private boolean banLatest = true; 094 095 /** 096 * Don't allow the RELEASE identifier. 097 */ 098 private boolean banRelease = true; 099 100 /** 101 * Don't allow snapshot plugins. 102 */ 103 private boolean banSnapshots = true; 104 105 /** 106 * Don't allow timestamp snapshot plugins. 107 */ 108 private boolean banTimestamps = true; 109 110 /** 111 * @since 3.0.0 112 */ 113 private boolean banMavenDefaults = true; 114 115 /** 116 * The comma separated list of phases that should be used to find lifecycle plugin bindings. The default value is 117 * "clean,deploy,site". 118 */ 119 private String phases = "clean,deploy,site"; 120 121 /** 122 * Additional plugins to enforce have versions. These are plugins that may not be in the poms but are used anyway, 123 * like help, eclipse etc. <br> 124 * The plugins should be specified in the form: <code>group:artifactId</code>. 125 */ 126 private List<String> additionalPlugins; 127 128 /** 129 * Plugins to skip for version enforcement. The plugins should be specified in the form: 130 * <code>group:artifactId</code>. NOTE: This is deprecated, use unCheckedPluginList instead. 131 */ 132 private List<String> unCheckedPlugins; 133 134 /** 135 * Same as unCheckedPlugins but as a comma list to better support properties. Sample form: 136 * <code>group:artifactId,group2:artifactId2</code> 137 * 138 * @since 1.0-beta-1 139 */ 140 private String unCheckedPluginList; 141 142 /** The phase to lifecycle map. */ 143 private Map<String, Lifecycle> phaseToLifecycleMap; 144 145 /** The lifecycles. */ 146 private Collection<Lifecycle> lifecycles; 147 148 /** The plugin manager. */ 149 private final PluginManager pluginManager; 150 151 /** The factory. */ 152 private final ArtifactFactory factory; 153 154 private final RepositorySystem repositorySystem; 155 156 /** The session. */ 157 private final MavenSession session; 158 159 /** The utils. */ 160 private final EnforcerRuleUtils utils; 161 162 private final RuntimeInformation runtimeInformation; 163 164 private final DefaultLifecycles defaultLifeCycles; 165 166 private final MavenProject project; 167 168 private final ExpressionEvaluator evaluator; 169 170 private final PlexusContainer container; 171 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 367 * @param additional the additional 368 * @return the sets the 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 for (String pluginString : additional) { 374 Plugin plugin = parsePluginString(pluginString, "AdditionalPlugins"); 375 376 if (existing == null) { 377 existing = new HashSet<>(); 378 existing.add(plugin); 379 } else if (!existing.contains(plugin)) { 380 existing.add(plugin); 381 } 382 } 383 } 384 return existing; 385 } 386 387 /** 388 * Helper method to parse and inject a Plugin. 389 * 390 * @param pluginString a plugin description to parse 391 * @param field a source of pluginString 392 * @return the prepared plugin 393 */ 394 private Plugin parsePluginString(String pluginString, String field) throws EnforcerRuleError { 395 if (pluginString != null) { 396 String[] pluginStrings = pluginString.split(":"); 397 if (pluginStrings.length == 2) { 398 Plugin plugin = new Plugin(); 399 plugin.setGroupId(StringUtils.strip(pluginStrings[0])); 400 plugin.setArtifactId(StringUtils.strip(pluginStrings[1])); 401 402 return plugin; 403 } else { 404 throw new EnforcerRuleError("Invalid " + field + " string: " + pluginString); 405 } 406 } else { 407 throw new EnforcerRuleError("Invalid " + field + " string: " + pluginString); 408 } 409 } 410 411 /** 412 * Finds the plugins that are listed in active profiles. 413 * 414 * @param project the project 415 * @return the profile plugins 416 */ 417 public Set<Plugin> getProfilePlugins(MavenProject project) { 418 Set<Plugin> result = new HashSet<>(); 419 List<Profile> profiles = project.getActiveProfiles(); 420 if (profiles != null && !profiles.isEmpty()) { 421 for (Profile p : profiles) { 422 BuildBase b = p.getBuild(); 423 if (b != null) { 424 List<Plugin> plugins = b.getPlugins(); 425 if (plugins != null) { 426 result.addAll(plugins); 427 } 428 } 429 } 430 } 431 return result; 432 } 433 434 /** 435 * Given a plugin, this will retrieve the matching plugin artifact from the model. 436 * 437 * @param plugin plugin to lookup 438 * @param project project to search 439 * @return matching plugin, <code>null</code> if not found. 440 */ 441 private Plugin findCurrentPlugin(Plugin plugin, MavenProject project) throws EnforcerRuleException { 442 Plugin found = null; 443 try { 444 Model model = project.getModel(); 445 Map<String, Plugin> plugins = model.getBuild().getPluginsAsMap(); 446 found = plugins.get(plugin.getKey()); 447 } catch (NullPointerException e) { 448 // nothing to do here 449 } 450 451 if (found == null) { 452 Artifact artifact = factory.createPluginArtifact( 453 plugin.getGroupId(), plugin.getArtifactId(), VersionRange.createFromVersion("LATEST")); 454 455 try { 456 repositorySystem.resolveArtifact( 457 session.getRepositorySession(), 458 new ArtifactRequest( 459 RepositoryUtils.toArtifact(artifact), 460 session.getCurrentProject().getRemotePluginRepositories(), 461 "resolvePlugin")); 462 } catch (ArtifactResolutionException e) { 463 throw new EnforcerRuleException("Unable to resolve the plugin " + artifact.getArtifactId(), e); 464 } 465 plugin.setVersion(artifact.getVersion()); 466 467 found = plugin; 468 } 469 470 return found; 471 } 472 473 /** 474 * Gets the plugins that are bound to the defined phases. This does not find plugins bound in the pom to a phase 475 * later than the plugin is executing. 476 * 477 * @param project the project 478 * @param thePhases the phases 479 * @return the bound plugins 480 * @throws PluginNotFoundException the plugin not found exception 481 * @throws LifecycleExecutionException the lifecycle execution exception 482 */ 483 private Set<Plugin> getBoundPlugins(MavenProject project, String thePhases) 484 throws PluginNotFoundException, LifecycleExecutionException { 485 486 Set<Plugin> allPlugins = new HashSet<>(); 487 488 // lookup the bindings for all the passed in phases 489 String[] lifecyclePhases = thePhases.split(","); 490 for (int i = 0; i < lifecyclePhases.length; i++) { 491 String lifecyclePhase = lifecyclePhases[i]; 492 if (lifecyclePhase != null && !lifecyclePhase.isEmpty()) { 493 try { 494 Lifecycle lifecycle = getLifecycleForPhase(lifecyclePhase); 495 getLog().debug("getBoundPlugins(): " + project.getId() + " " + lifecyclePhase + " " 496 + lifecycle.getId()); 497 allPlugins.addAll(getAllPlugins(project, lifecycle)); 498 } catch (BuildFailureException e) { 499 // i'm going to swallow this because the 500 // user may have declared a phase that 501 // doesn't exist for every module. 502 } 503 } 504 } 505 return allPlugins; 506 } 507 508 /** 509 * Checks for valid version specified. Checks to see if the version is specified for the plugin. Can optionally ban 510 * "RELEASE" or "LATEST" even if specified. 511 * 512 * @param source the source 513 * @param pluginWrappers the plugins 514 * @return true, if successful 515 */ 516 public boolean hasValidVersionSpecified(Plugin source, List<PluginWrapper> pluginWrappers) { 517 boolean found = false; 518 boolean status = false; 519 for (PluginWrapper plugin : pluginWrappers) { 520 // find the matching plugin entry 521 if (isMatchingPlugin(source, plugin)) { 522 found = true; 523 // found the entry. now see if the version is specified 524 String version = plugin.getVersion(); 525 try { 526 version = (String) evaluator.evaluate(version); 527 } catch (ExpressionEvaluationException e) { 528 return false; 529 } 530 531 if (isValidVersion(version)) { 532 getLog().debug("checking for notEmpty and notIsWhitespace(): " + version); 533 if (banRelease && version.equals("RELEASE")) { 534 return false; 535 } 536 537 if (banLatest && version.equals("LATEST")) { 538 return false; 539 } 540 541 if (banSnapshots && isSnapshot(version)) { 542 return false; 543 } 544 // the version was specified and not 545 // banned. It's ok. Keep looking through the list to make 546 // sure it's not using a banned version somewhere else. 547 548 status = true; 549 550 if (!banRelease && !banLatest && !banSnapshots) { 551 // no need to keep looking 552 break; 553 } 554 } 555 } 556 } 557 if (!found) { 558 getLog().debug("plugin " + source.getGroupId() + ":" + source.getArtifactId() + " not found"); 559 } 560 return status; 561 } 562 563 private boolean isValidVersion(String version) { 564 return (version != null && !version.isEmpty()) && !StringUtils.isWhitespace(version); 565 } 566 567 private boolean isMatchingPlugin(Plugin source, PluginWrapper plugin) { 568 return source.getArtifactId().equals(plugin.getArtifactId()) 569 && source.getGroupId().equals(plugin.getGroupId()); 570 } 571 572 /** 573 * Checks if is snapshot. 574 * 575 * @param baseVersion the base version 576 * @return true, if is snapshot 577 */ 578 private boolean isSnapshot(String baseVersion) { 579 if (banTimestamps) { 580 return Artifact.VERSION_FILE_PATTERN.matcher(baseVersion).matches() 581 || baseVersion.endsWith(Artifact.SNAPSHOT_VERSION); 582 } else { 583 return baseVersion.endsWith(Artifact.SNAPSHOT_VERSION); 584 } 585 } 586 587 /* 588 * Uses borrowed lifecycle code to get a list of all plugins bound to the lifecycle. 589 */ 590 591 /** 592 * Gets the all plugins. 593 * 594 * @param project the project 595 * @param lifecycle the lifecycle 596 * @return the all plugins 597 * @throws PluginNotFoundException the plugin not found exception 598 * @throws LifecycleExecutionException the lifecycle execution exception 599 */ 600 private Set<Plugin> getAllPlugins(MavenProject project, Lifecycle lifecycle) 601 throws PluginNotFoundException, LifecycleExecutionException { 602 603 getLog().debug("RequirePluginVersions.getAllPlugins:"); 604 605 Set<Plugin> plugins = new HashSet<>(); 606 // first, bind those associated with the packaging 607 Map<String, String> mappings = findMappingsForLifecycle(project, lifecycle); 608 609 for (Map.Entry<String, String> entry : mappings.entrySet()) { 610 getLog().debug(" lifecycleMapping = " + entry.getKey()); 611 String pluginsForLifecycle = (String) entry.getValue(); 612 getLog().debug(" plugins = " + pluginsForLifecycle); 613 if (pluginsForLifecycle != null && !pluginsForLifecycle.isEmpty()) { 614 String pluginList[] = pluginsForLifecycle.split(","); 615 for (String plugin : pluginList) { 616 plugin = StringUtils.strip(plugin); 617 getLog().debug(" plugin = " + plugin); 618 String tokens[] = plugin.split(":"); 619 getLog().debug(" GAV = " + Arrays.asList(tokens)); 620 621 Plugin p = new Plugin(); 622 p.setGroupId(tokens[0]); 623 p.setArtifactId(tokens[1]); 624 plugins.add(p); 625 } 626 } 627 } 628 629 plugins.addAll(project.getBuildPlugins()); 630 631 return plugins; 632 } 633 634 /* 635 * NOTE: All the code following this point was scooped from the DefaultLifecycleExecutor. There must be a better way 636 * but for now it should work. 637 */ 638 639 /** 640 * Gets the phase to lifecycle map. 641 * 642 * @return the phase to lifecycle map 643 * @throws LifecycleExecutionException the lifecycle execution exception 644 */ 645 public Map<String, Lifecycle> getPhaseToLifecycleMap() throws LifecycleExecutionException { 646 if (phaseToLifecycleMap == null) { 647 phaseToLifecycleMap = new HashMap<>(); 648 649 for (Lifecycle lifecycle : lifecycles) { 650 List<String> phases = lifecycle.getPhases(); 651 for (String phase : phases) { 652 getLog().debug("getPhaseToLifecycleMap(): phase: " + phase); 653 if (phaseToLifecycleMap.containsKey(phase)) { 654 Lifecycle prevLifecycle = (Lifecycle) phaseToLifecycleMap.get(phase); 655 throw new LifecycleExecutionException("Phase '" + phase 656 + "' is defined in more than one lifecycle: '" + lifecycle.getId() + "' and '" 657 + prevLifecycle.getId() + "'"); 658 } else { 659 phaseToLifecycleMap.put(phase, lifecycle); 660 } 661 } 662 } 663 } 664 return phaseToLifecycleMap; 665 } 666 667 /** 668 * Gets the lifecycle for phase. 669 * 670 * @param phase the phase 671 * @return the lifecycle for phase 672 * @throws BuildFailureException the build failure exception 673 * @throws LifecycleExecutionException the lifecycle execution exception 674 */ 675 private Lifecycle getLifecycleForPhase(String phase) throws BuildFailureException, LifecycleExecutionException { 676 Lifecycle lifecycle = getPhaseToLifecycleMap().get(phase); 677 678 if (lifecycle == null) { 679 throw new BuildFailureException("Unable to find lifecycle for phase '" + phase + "'"); 680 } 681 return lifecycle; 682 } 683 684 /** 685 * Find mappings for lifecycle. 686 * 687 * @param project the project 688 * @param lifecycle the lifecycle 689 * @return the map 690 * @throws LifecycleExecutionException the lifecycle execution exception 691 * @throws PluginNotFoundException the plugin not found exception 692 */ 693 private Map<String, String> findMappingsForLifecycle(MavenProject project, Lifecycle lifecycle) 694 throws LifecycleExecutionException, PluginNotFoundException { 695 String packaging = project.getPackaging(); 696 Map<String, String> mappings = null; 697 698 LifecycleMapping m = (LifecycleMapping) findExtension( 699 project, LifecycleMapping.ROLE, packaging, session.getSettings(), session.getLocalRepository()); 700 if (m != null) { 701 mappings = m.getPhases(lifecycle.getId()); 702 } 703 704 Map<String, String> defaultMappings = lifecycle.getDefaultPhases(); 705 706 if (mappings == null) { 707 try { 708 m = container.lookup(LifecycleMapping.class, packaging); 709 mappings = m.getPhases(lifecycle.getId()); 710 } catch (ComponentLookupException e) { 711 if (defaultMappings == null) { 712 throw new LifecycleExecutionException( 713 "Cannot find lifecycle mapping for packaging: \'" + packaging + "\'.", e); 714 } 715 } 716 } 717 718 if (mappings == null) { 719 if (defaultMappings == null) { 720 throw new LifecycleExecutionException( 721 "Cannot find lifecycle mapping for packaging: \'" + packaging + "\', and there is no default"); 722 } else { 723 mappings = defaultMappings; 724 } 725 } 726 727 return mappings; 728 } 729 730 /** 731 * Find extension. 732 * 733 * @param project the project 734 * @param role the role 735 * @param roleHint the role hint 736 * @param settings the settings 737 * @param localRepository the local repository 738 * @return the object 739 * @throws LifecycleExecutionException the lifecycle execution exception 740 * @throws PluginNotFoundException the plugin not found exception 741 */ 742 private Object findExtension( 743 MavenProject project, String role, String roleHint, Settings settings, ArtifactRepository localRepository) 744 throws LifecycleExecutionException, PluginNotFoundException { 745 Object pluginComponent = null; 746 747 List<Plugin> buildPlugins = project.getBuildPlugins(); 748 for (Plugin plugin : buildPlugins) { 749 if (plugin.isExtensions()) { 750 verifyPlugin(plugin, project, settings, localRepository); 751 752 // TODO: if moved to the plugin manager we 753 // already have the descriptor from above 754 // and so do can lookup the container 755 // directly 756 try { 757 pluginComponent = pluginManager.getPluginComponent(plugin, role, roleHint); 758 759 if (pluginComponent != null) { 760 break; 761 } 762 } catch (ComponentLookupException e) { 763 getLog().debug("Unable to find the lifecycle component in the extension " + e.getMessage()); 764 } catch (PluginManagerException e) { 765 throw new LifecycleExecutionException( 766 "Error getting extensions from the plugin '" + plugin.getKey() + "': " + e.getMessage(), e); 767 } 768 } 769 } 770 return pluginComponent; 771 } 772 773 /** 774 * Verify plugin. 775 * 776 * @param plugin the plugin 777 * @param project the project 778 * @param settings the settings 779 * @param localRepository the local repository 780 * @return the plugin descriptor 781 * @throws LifecycleExecutionException the lifecycle execution exception 782 * @throws PluginNotFoundException the plugin not found exception 783 */ 784 private PluginDescriptor verifyPlugin( 785 Plugin plugin, MavenProject project, Settings settings, ArtifactRepository localRepository) 786 throws LifecycleExecutionException, PluginNotFoundException { 787 PluginDescriptor pluginDescriptor; 788 try { 789 pluginDescriptor = pluginManager.verifyPlugin(plugin, project, settings, localRepository); 790 } catch (PluginManagerException e) { 791 throw new LifecycleExecutionException( 792 "Internal error in the plugin manager getting plugin '" + plugin.getKey() + "': " + e.getMessage(), 793 e); 794 } catch (PluginVersionResolutionException 795 | InvalidVersionSpecificationException 796 | InvalidPluginException 797 | PluginVersionNotFoundException 798 | org.apache.maven.artifact.resolver.ArtifactResolutionException 799 | ArtifactNotFoundException e) { 800 throw new LifecycleExecutionException(e.getMessage(), e); 801 } 802 return pluginDescriptor; 803 } 804 805 /** 806 * Gets all plugin entries in build.plugins, build.pluginManagement.plugins, profile.build.plugins, reporting and 807 * profile.reporting in this project and all parents 808 * 809 * @param project the project 810 * @return the all plugin entries wrapped in a PluginWrapper Object 811 */ 812 private List<PluginWrapper> getAllPluginEntries(MavenProject project) { 813 List<PluginWrapper> plugins = new ArrayList<>(); 814 // now find all the plugin entries, either in 815 // build.plugins or build.pluginManagement.plugins, profiles.plugins and reporting 816 817 getPlugins(plugins, project.getModel()); 818 getReportingPlugins(plugins, project.getModel()); 819 getPluginManagementPlugins(plugins, project.getModel()); 820 addPluginsInProfiles(plugins, project.getModel()); 821 822 return plugins; 823 } 824 825 private void addPluginsInProfiles(List<PluginWrapper> plugins, Model model) { 826 List<Profile> profiles = ofNullable(model).map(Model::getProfiles).orElseGet(Collections::emptyList); 827 for (Profile profile : profiles) { 828 getProfilePlugins(plugins, profile); 829 getProfileReportingPlugins(plugins, profile); 830 getProfilePluginManagementPlugins(plugins, profile); 831 } 832 } 833 834 private void getProfilePluginManagementPlugins(List<PluginWrapper> plugins, Profile profile) { 835 List<Plugin> modelPlugins = ofNullable(profile) 836 .map(Profile::getBuild) 837 .map(PluginConfiguration::getPluginManagement) 838 .map(PluginContainer::getPlugins) 839 .orElseGet(Collections::emptyList); 840 plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults)); 841 } 842 843 private void getProfileReportingPlugins(List<PluginWrapper> plugins, Profile profile) { 844 List<ReportPlugin> modelReportPlugins = ofNullable(profile) 845 .map(ModelBase::getReporting) 846 .map(Reporting::getPlugins) 847 .orElseGet(Collections::emptyList); 848 // add the reporting plugins 849 plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults)); 850 } 851 852 private void getProfilePlugins(List<PluginWrapper> plugins, Profile profile) { 853 List<Plugin> modelPlugins = ofNullable(profile) 854 .map(Profile::getBuild) 855 .map(PluginContainer::getPlugins) 856 .orElseGet(Collections::emptyList); 857 plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults)); 858 } 859 860 private void getPlugins(List<PluginWrapper> plugins, Model model) { 861 List<Plugin> modelPlugins = ofNullable(model) 862 .map(Model::getBuild) 863 .map(PluginContainer::getPlugins) 864 .orElseGet(Collections::emptyList); 865 plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults)); 866 } 867 868 private void getPluginManagementPlugins(List<PluginWrapper> plugins, Model model) { 869 List<Plugin> modelPlugins = ofNullable(model) 870 .map(Model::getBuild) 871 .map(PluginConfiguration::getPluginManagement) 872 .map(PluginContainer::getPlugins) 873 .orElseGet(Collections::emptyList); 874 plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults)); 875 } 876 877 private void getReportingPlugins(List<PluginWrapper> plugins, Model model) { 878 List<ReportPlugin> modelReportPlugins = ofNullable(model) 879 .map(ModelBase::getReporting) 880 .map(Reporting::getPlugins) 881 .orElseGet(Collections::emptyList); 882 // add the reporting plugins 883 plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults)); 884 } 885 886 /** 887 * Sets the ban latest. 888 * 889 * @param theBanLatest the banLatest to set 890 */ 891 public void setBanLatest(boolean theBanLatest) { 892 this.banLatest = theBanLatest; 893 } 894 895 /** 896 * Sets the ban release. 897 * 898 * @param theBanRelease the banRelease to set 899 */ 900 public void setBanRelease(boolean theBanRelease) { 901 this.banRelease = theBanRelease; 902 } 903 904 /** 905 * Checks if is ban snapshots. 906 * 907 * @return the banSnapshots 908 */ 909 public boolean isBanSnapshots() { 910 return this.banSnapshots; 911 } 912 913 /** 914 * Sets the ban snapshots. 915 * 916 * @param theBanSnapshots the banSnapshots to set 917 */ 918 public void setBanSnapshots(boolean theBanSnapshots) { 919 this.banSnapshots = theBanSnapshots; 920 } 921 922 /** 923 * Sets the ban timestamps. 924 * 925 * @param theBanTimestamps the banTimestamps to set 926 */ 927 public void setBanTimestamps(boolean theBanTimestamps) { 928 this.banTimestamps = theBanTimestamps; 929 } 930 931 @Override 932 public String toString() { 933 return String.format( 934 "RequirePluginVersions[message=%s, banLatest=%b, banRelease=%b, banSnapshots=%b, banTimestamps=%b, phases=%s, additionalPlugins=%s, unCheckedPluginList=%s, unCheckedPlugins=%s]", 935 getMessage(), 936 banLatest, 937 banRelease, 938 banSnapshots, 939 banTimestamps, 940 phases, 941 additionalPlugins, 942 unCheckedPluginList, 943 unCheckedPlugins); 944 } 945}