001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014 015package org.basepom.mojo.dvc; 016 017import static com.google.common.base.Preconditions.checkNotNull; 018import static com.google.common.base.Preconditions.checkState; 019 020import org.basepom.mojo.dvc.dependency.DependencyMap; 021import org.basepom.mojo.dvc.dependency.DependencyMapBuilder; 022import org.basepom.mojo.dvc.dependency.DependencyTreeResolver; 023import org.basepom.mojo.dvc.model.ResolverDefinition; 024import org.basepom.mojo.dvc.model.VersionCheckExcludes; 025import org.basepom.mojo.dvc.strategy.StrategyProvider; 026import org.basepom.mojo.dvc.version.VersionResolutionCollection; 027 028import java.util.Arrays; 029import java.util.List; 030 031import com.google.common.base.Strings; 032import com.google.common.collect.ImmutableList; 033import com.google.common.collect.ImmutableSet; 034import com.google.common.collect.ImmutableSetMultimap; 035import org.apache.maven.execution.MavenSession; 036import org.apache.maven.plugin.AbstractMojo; 037import org.apache.maven.plugin.MojoExecutionException; 038import org.apache.maven.plugin.MojoFailureException; 039import org.apache.maven.plugins.annotations.Component; 040import org.apache.maven.plugins.annotations.Parameter; 041import org.apache.maven.project.DefaultProjectBuildingRequest; 042import org.apache.maven.project.MavenProject; 043import org.apache.maven.project.ProjectBuilder; 044import org.apache.maven.project.ProjectBuildingRequest; 045import org.apache.maven.project.ProjectDependenciesResolver; 046import org.eclipse.aether.DefaultRepositorySystemSession; 047import org.eclipse.aether.RepositorySystem; 048import org.eclipse.aether.RepositorySystemSession; 049import org.eclipse.aether.artifact.Artifact; 050import org.eclipse.aether.resolution.VersionRangeRequest; 051import org.eclipse.aether.util.artifact.JavaScopes; 052import org.eclipse.aether.util.graph.version.SnapshotVersionFilter; 053 054/** 055 * Base code for all the mojos. Contains the dependency resolvers and the common options. 056 */ 057public abstract class AbstractDependencyVersionsMojo 058 extends AbstractMojo 059 implements Context { 060 061 private static final ImmutableSet<String> VALID_SCOPES = ImmutableSet.of( 062 ScopeLimitingFilter.COMPILE_PLUS_RUNTIME, 063 JavaScopes.COMPILE, 064 JavaScopes.RUNTIME, 065 JavaScopes.TEST); 066 067 protected final PluginLog log = new PluginLog(this.getClass()); 068 069 @Parameter(defaultValue = "${project}", readonly = true) 070 public MavenProject project; 071 072 @Parameter(defaultValue = "${session}", readonly = true) 073 public MavenSession mavenSession; 074 075 @Parameter(defaultValue = "${reactorProjects}", readonly = true, required = true) 076 public List<MavenProject> reactorProjects; 077 078 @Component 079 public ProjectBuilder mavenProjectBuilder; 080 081 @Component 082 public ProjectDependenciesResolver projectDependenciesResolver; 083 084 @Component 085 public RepositorySystem repositorySystem; 086 087 /** 088 * The strategy provider. This can be requested by other pieces to add additional strategies. 089 */ 090 @Component 091 public StrategyProvider strategyProvider; 092 093 /** 094 * List of version checks that will be removed from the version check. This allows potential conflicts to be excluded. 095 * <br> 096 * <pre> 097 * <exclusions> 098 * <exclusion> 099 * <dependency>...</dependency> 100 * <expected>...</expected> 101 * <resolved>...</resolved> 102 * </exclusion> 103 * </exclusions> 104 * </pre> 105 * <p> 106 * Each element consists of a dependency pattern <code>[groupId]:[artifactId]</code> that supports wildcards and an expected version (which is the version 107 * is expected by the artifact) and a resolved version (the version that the dependency resolution has chosen). 108 * </p> 109 */ 110 @Parameter(alias = "exceptions") 111 public VersionCheckExcludes[] exclusions = new VersionCheckExcludes[0]; 112 113 /** 114 * Skip the plugin execution. 115 */ 116 @Parameter(defaultValue = "false", property = "dvc.skip") 117 public boolean skip = false; 118 119 /** 120 * Include POM projects when running on a multi-module project. Dependency resolution on a pom project almost never makes sense as it does not actually 121 * build any artifacts. 122 * 123 * @since 3.0.0 124 */ 125 @Parameter(defaultValue = "false", property = "dvc.include-pom-projects") 126 public boolean includePomProjects = false; 127 128 /** 129 * Silence all non-output and non-error messages. 130 * 131 * @since 3.0.0 132 */ 133 @Parameter(defaultValue = "false", property = "dvc.quiet") 134 public boolean quiet = false; 135 136 /** 137 * Dependency resolution scope. Defaults to <code>test</code>. Valid choices are <code>compile+runtime</code>, <code>compile</code>, 138 * <code>test</code> and <code>runtime</code>. 139 * 140 * @since 3.0.0 141 */ 142 @Parameter(defaultValue = "test", property = "scope") 143 public String scope = JavaScopes.TEST; 144 145 /** 146 * Use deep scan or regular scan. Deep scan looks at all dependencies in the dependency tree, while regular scan only looks one level deep into the direct 147 * dependencies. 148 * 149 * @since 3.0.0 150 */ 151 @Parameter(defaultValue = "false", property = "dvc.deep-scan") 152 public boolean deepScan = false; 153 154 /** 155 * List only direct dependencies or all dependencies. 156 * 157 * @since 3.0.0 158 */ 159 @Parameter(defaultValue = "false", property = "dvc.direct-only") 160 public boolean directOnly = false; 161 162 /** 163 * List only managed dependencies or all dependencies. 164 * 165 * @since 3.0.0 166 */ 167 @Parameter(defaultValue = "false", property = "dvc.managed-only") 168 public boolean managedOnly = false; 169 170 /** 171 * Run dependency resolution in parallel with multiple threads. Should only ever set to <code>false</code> if the plugin shows stability problems when 172 * resolving dependencies. Please <a href="issue-management.html">file a bug</a> in that case, too. 173 * 174 * @since 3.0.0 175 */ 176 @Parameter(defaultValue = "true", property = "dvc.fast-resolution") 177 public boolean fastResolution = true; 178 179 /** 180 * Fail the build if an artifact in <code>system</code> scope can not be resolved. Those are notoriously dependent on the local build environment and some 181 * outright fail (e.g. referencing the <code>tools.jar</code>, which no longer exists in a JDK8+ environment). 182 * <br> 183 * Setting this flag to <code>true</code> will fail the build if any <code>system</code> scoped artifact can not be resolved. This is almost never desired, 184 * except when building a project with a direct <code>system</code> scoped dependency. 185 * 186 * @since 3.0.0 187 */ 188 @Parameter(defaultValue = "false", property = "dvc.unresolved-system-artifacts-fail-build") 189 protected boolean unresolvedSystemArtifactsFailBuild = false; 190 191 /** 192 * Require all optional dependencies to exist and fail the build if any optional dependency can not resolved. This is almost never needed and actually 193 * causes problems for some projects that use large public dependencies from central that in turn pull in non-public dependencies as optional. 194 * 195 * @since 3.2.0 196 */ 197 @Parameter(defaultValue = "false", property = "dvc.optional-dependencies-must-exist") 198 protected boolean optionalDependenciesMustExist = false; 199 200 201 /** 202 * List of resolvers to apply specific strategies to dependencies. 203 * 204 * <pre> 205 * <resolvers> 206 * <resolver> 207 * <strategy>...<strategy> 208 * <includes> 209 * <include>...<include> 210 * <includes> 211 * <resolver> 212 * <resolvers> 213 * </pre> 214 * <p> 215 * A resolver maps a specific strategy to a list of includes. The include syntax is <code>[group-id]:[artifact-id]</code> where each pattern segment 216 * supports full and partial wildcards (<code>*</code>). 217 * <br> 218 * The plugin includes some default strategies: <code>apr</code>, <code>default</code>, <code>single-digit</code> and 219 * <code>two-digits-backward-compatible</code>. Additional strategies can be defined and added to the plugin classpath. 220 */ 221 @Parameter 222 public ResolverDefinition[] resolvers = new ResolverDefinition[0]; 223 224 /** 225 * Sets the default strategy to use to evaluate whether two dependency versions are compatible or not. 226 * <p> 227 * The <code>default</code> resolution strategy matches the Maven dependency resolution itself; any two dependencies that maven considers compatible will be 228 * accepted. 229 * 230 * @since 3.0.0 231 */ 232 @Parameter(defaultValue = "default", property = "dvc.default-strategy") 233 public String defaultStrategy = "default"; 234 235 protected StrategyCache strategyCache; 236 protected RepositorySystemSession snapshotFilteredSession; 237 238 @Override 239 @SuppressWarnings("PMD.AvoidRethrowingException") 240 public void execute() 241 throws MojoExecutionException, MojoFailureException { 242 try { 243 for (VersionCheckExcludes exclusion : exclusions) { 244 checkState(exclusion.isValid(), "Invalid exclusion specification: '%s'", exclusion); 245 } 246 247 checkState(!Strings.nullToEmpty(scope).trim().isEmpty() && VALID_SCOPES.contains(scope), "Scope '%s' is invalid", scope); 248 249 if (skip) { 250 log.report(quiet, "Skipping plugin execution"); 251 return; 252 } 253 254 if (!includePomProjects && "pom".equals(project.getPackaging())) { 255 log.report(quiet, "Ignoring POM project"); 256 return; 257 } 258 259 log.debug("Starting %s mojo run!", this.getClass().getSimpleName()); 260 261 this.strategyCache = new StrategyCache(strategyProvider, resolvers, defaultStrategy); 262 this.snapshotFilteredSession = new DefaultRepositorySystemSession(mavenSession.getRepositorySession()) 263 .setVersionFilter(new SnapshotVersionFilter()); 264 265 final ScopeLimitingFilter scopeFilter = createScopeFilter(); 266 final DependencyMap rootDependencyMap = new DependencyMapBuilder(this).mapProject(project, scopeFilter); 267 268 try (DependencyTreeResolver dependencyTreeResolver = new DependencyTreeResolver(this, rootDependencyMap)) { 269 final ImmutableSetMultimap<QualifiedName, VersionResolutionCollection> resolutionMap = dependencyTreeResolver.computeResolutionMap(project, 270 scopeFilter); 271 doExecute(resolutionMap, rootDependencyMap); 272 } 273 } catch (MojoExecutionException | MojoFailureException e) { 274 throw e; 275 } catch (Exception e) { 276 throw new MojoExecutionException("While running mojo: ", e); 277 } finally { 278 log.debug("Ended %s mojo run!", this.getClass().getSimpleName()); 279 } 280 } 281 282 /** 283 * Subclasses need to implement this method. 284 * 285 * @param resolutionMap The prebuilt resolution map from qualified names to version resolution collections. 286 * @param rootDependencyMap The prebuilt dependency map for all the root dependencies. 287 * @throws Exception When an execution error occurs. 288 */ 289 protected abstract void doExecute(ImmutableSetMultimap<QualifiedName, VersionResolutionCollection> resolutionMap, DependencyMap rootDependencyMap) 290 throws Exception; 291 292 /** 293 * Defines the scope used to resolve the project dependencies. The project dependencies will be limited to the dependencies that match this filter. The list 294 * mojo overrides this to limit the scope in which dependencies are listed. By default, include everything. 295 * 296 * @return The {@link ScopeLimitingFilter} instance for the project dependencies. 297 */ 298 protected ScopeLimitingFilter createScopeFilter() { 299 return ScopeLimitingFilter.computeDependencyScope(scope); 300 } 301 302 @Override 303 public boolean useFastResolution() { 304 return fastResolution; 305 } 306 307 @Override 308 public boolean useDeepScan() { 309 return deepScan; 310 } 311 312 @Override 313 public boolean isOptionalDependenciesMustExist() { 314 return optionalDependenciesMustExist; 315 } 316 317 @Override 318 public StrategyCache getStrategyCache() { 319 return strategyCache; 320 } 321 322 @Override 323 public ProjectBuilder getProjectBuilder() { 324 return mavenProjectBuilder; 325 } 326 327 @Override 328 public ProjectDependenciesResolver getProjectDependenciesResolver() { 329 return projectDependenciesResolver; 330 } 331 332 @Override 333 public MavenProject getRootProject() { 334 return project; 335 } 336 337 @Override 338 public List<MavenProject> getReactorProjects() { 339 return ImmutableList.copyOf(reactorProjects); 340 } 341 342 @Override 343 public RepositorySystemSession getRepositorySystemSession() { 344 return snapshotFilteredSession; 345 } 346 347 @Override 348 public RepositorySystem getRepositorySystem() { 349 return repositorySystem; 350 } 351 352 @Override 353 public ProjectBuildingRequest createProjectBuildingRequest() { 354 DefaultProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(mavenSession.getProjectBuildingRequest()); 355 buildingRequest.setRemoteRepositories(project.getRemoteArtifactRepositories()); 356 return buildingRequest; 357 } 358 359 @Override 360 public VersionRangeRequest createVersionRangeRequest(Artifact artifact) { 361 checkNotNull(artifact, "artifact is null"); 362 return new VersionRangeRequest(artifact, project.getRemoteProjectRepositories(), ""); 363 } 364 365 @Override 366 public List<VersionCheckExcludes> getExclusions() { 367 return Arrays.asList(exclusions); 368 } 369 370 @Override 371 public boolean isUnresolvedSystemArtifactsFailBuild() { 372 return unresolvedSystemArtifactsFailBuild; 373 } 374}