View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.enforcer.rules;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Objects;
28  
29  import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
30  import org.apache.maven.enforcer.rules.utils.ArtifactMatcher;
31  import org.apache.maven.model.Dependency;
32  import org.apache.maven.model.DependencyManagement;
33  import org.apache.maven.project.MavenProject;
34  
35  /**
36   * This rule bans all scope values except for {@code import} from dependencies within the dependency management.
37   * There is a configuration option to ignore certain dependencies in this check.
38   */
39  @Named("banDependencyManagementScope")
40  public final class BanDependencyManagementScope extends AbstractStandardEnforcerRule {
41  
42      /**
43       * Specify the dependencies that will be ignored. This can be a list of artifacts in the format
44       * <code>groupId[:artifactId][:version][:type][:scope]</code>. Wildcard '*' can be used to in place of specific
45       * section (ie group:*:1.0 will match both 'group:artifact:1.0' and 'group:anotherArtifact:1.0'). Version is a
46       * string representing standard Maven version range. Empty patterns will be ignored.
47       *
48       * @see {@link #setExcludes(List)}
49       */
50      private List<String> excludes = null;
51  
52      /**
53       * If {@code true} the dependencyManagement from imported dependencyManagement and parent pom's is checked as well,
54       * otherwise only the local dependencyManagement defined in the current project's pom.xml.
55       */
56      private boolean checkEffectivePom = false;
57  
58      private final MavenProject project;
59  
60      @Inject
61      public BanDependencyManagementScope(MavenProject project) {
62          this.project = Objects.requireNonNull(project);
63      }
64  
65      @Override
66      public void execute() throws EnforcerRuleException {
67          // only evaluate local depMgmt, without taking into account inheritance and interpolation
68          DependencyManagement depMgmt = checkEffectivePom
69                  ? project.getModel().getDependencyManagement()
70                  : project.getOriginalModel().getDependencyManagement();
71          if (depMgmt != null && depMgmt.getDependencies() != null) {
72              List<Dependency> violatingDependencies = getViolatingDependencies(depMgmt);
73              if (!violatingDependencies.isEmpty()) {
74                  String message = getMessage();
75                  StringBuilder buf = new StringBuilder();
76                  if (message == null) {
77                      message = "Scope other than 'import' is not allowed in 'dependencyManagement'";
78                  }
79                  buf.append(message + System.lineSeparator());
80                  for (Dependency violatingDependency : violatingDependencies) {
81                      buf.append(getErrorMessage(project, violatingDependency));
82                  }
83                  throw new EnforcerRuleException(buf.toString());
84              }
85          }
86      }
87  
88      protected List<Dependency> getViolatingDependencies(DependencyManagement depMgmt) {
89          final ArtifactMatcher excludesMatcher;
90          if (excludes != null) {
91              excludesMatcher = new ArtifactMatcher(excludes, Collections.emptyList());
92          } else {
93              excludesMatcher = null;
94          }
95          List<Dependency> violatingDependencies = new ArrayList<>();
96          for (Dependency dependency : depMgmt.getDependencies()) {
97              if (dependency.getScope() != null && !"import".equals(dependency.getScope())) {
98                  if (excludesMatcher != null && excludesMatcher.match(dependency)) {
99                      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 }