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.Collections; 026import java.util.List; 027import java.util.Objects; 028 029import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 030import org.apache.maven.enforcer.rules.utils.ArtifactMatcher; 031import org.apache.maven.model.Dependency; 032import org.apache.maven.model.DependencyManagement; 033import org.apache.maven.project.MavenProject; 034 035/** 036 * This rule bans all scope values except for {@code import} from dependencies within the dependency management. 037 * There is a configuration option to ignore certain dependencies in this check. 038 */ 039@Named("banDependencyManagementScope") 040public final class BanDependencyManagementScope extends AbstractStandardEnforcerRule { 041 042 /** 043 * Specify the dependencies that will be ignored. This can be a list of artifacts in the format 044 * <code>groupId[:artifactId][:version][:type][:scope]</code>. Wildcard '*' can be used to in place of specific 045 * section (ie group:*:1.0 will match both 'group:artifact:1.0' and 'group:anotherArtifact:1.0'). Version is a 046 * string representing standard Maven version range. Empty patterns will be ignored. 047 * 048 * @see {@link #setExcludes(List)} 049 */ 050 private List<String> excludes = null; 051 052 /** 053 * If {@code true} the dependencyManagement from imported dependencyManagement and parent pom's is checked as well, 054 * otherwise only the local dependencyManagement defined in the current project's pom.xml. 055 */ 056 private boolean checkEffectivePom = false; 057 058 private final MavenProject project; 059 060 @Inject 061 public BanDependencyManagementScope(MavenProject project) { 062 this.project = Objects.requireNonNull(project); 063 } 064 065 @Override 066 public void execute() throws EnforcerRuleException { 067 // only evaluate local depMgmt, without taking into account inheritance and interpolation 068 DependencyManagement depMgmt = checkEffectivePom 069 ? project.getModel().getDependencyManagement() 070 : project.getOriginalModel().getDependencyManagement(); 071 if (depMgmt != null && depMgmt.getDependencies() != null) { 072 List<Dependency> violatingDependencies = getViolatingDependencies(depMgmt); 073 if (!violatingDependencies.isEmpty()) { 074 String message = getMessage(); 075 StringBuilder buf = new StringBuilder(); 076 if (message == null) { 077 message = "Scope other than 'import' is not allowed in 'dependencyManagement'"; 078 } 079 buf.append(message + System.lineSeparator()); 080 for (Dependency violatingDependency : violatingDependencies) { 081 buf.append(getErrorMessage(project, violatingDependency)); 082 } 083 throw new EnforcerRuleException(buf.toString()); 084 } 085 } 086 } 087 088 protected List<Dependency> getViolatingDependencies(DependencyManagement depMgmt) { 089 final ArtifactMatcher excludesMatcher; 090 if (excludes != null) { 091 excludesMatcher = new ArtifactMatcher(excludes, Collections.emptyList()); 092 } else { 093 excludesMatcher = null; 094 } 095 List<Dependency> violatingDependencies = new ArrayList<>(); 096 for (Dependency dependency : depMgmt.getDependencies()) { 097 if (dependency.getScope() != null && !"import".equals(dependency.getScope())) { 098 if (excludesMatcher != null && excludesMatcher.match(dependency)) { 099 getLog().debug("Skipping excluded dependency " + dependency + " with scope " 100 + dependency.getScope()); 101 continue; 102 } 103 violatingDependencies.add(dependency); 104 } 105 } 106 return violatingDependencies; 107 } 108 109 private static CharSequence getErrorMessage(MavenProject project, Dependency violatingDependency) { 110 return "Banned scope '" + violatingDependency.getScope() + "' used on dependency '" 111 + violatingDependency.getManagementKey() + "' @ " 112 + formatLocation(project, violatingDependency.getLocation("")) 113 + System.lineSeparator(); 114 } 115 116 public void setExcludes(List<String> theExcludes) { 117 this.excludes = theExcludes; 118 } 119 120 @Override 121 public String toString() { 122 return String.format( 123 "BanDependencyManagementScope[message=%s, excludes=%s, checkEffectivePom=%b]", 124 getMessage(), excludes, checkEffectivePom); 125 } 126}