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.Collection;
26  import java.util.HashSet;
27  import java.util.LinkedHashMap;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Objects;
31  import java.util.Set;
32  import java.util.regex.Pattern;
33  
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
36  import org.apache.maven.execution.MavenSession;
37  import org.apache.maven.project.MavenProject;
38  
39  /**
40   * @author Robert Scholte
41   * @since 1.3
42   */
43  @Named("requireSameVersions")
44  public final class RequireSameVersions extends AbstractStandardEnforcerRule {
45      private boolean uniqueVersions;
46  
47      private Set<String> dependencies = new HashSet<>();
48  
49      private Set<String> plugins = new HashSet<>();
50  
51      private Set<String> buildPlugins = new HashSet<>();
52  
53      private Set<String> reportPlugins = new HashSet<>();
54  
55      private boolean sameModuleVersions;
56  
57      private final MavenProject project;
58  
59      private final MavenSession session;
60  
61      @Inject
62      public RequireSameVersions(MavenProject project, MavenSession session) {
63          this.project = Objects.requireNonNull(project);
64          this.session = Objects.requireNonNull(session);
65      }
66  
67      @Override
68      public void execute() throws EnforcerRuleException {
69          // consider including profile based artifacts
70          Map<String, List<String>> versionMembers = new LinkedHashMap<>();
71  
72          Set<String> allBuildPlugins = new HashSet<>(buildPlugins);
73          allBuildPlugins.addAll(plugins);
74          Set<String> allReportPlugins = new HashSet<>(reportPlugins);
75          allReportPlugins.addAll(plugins);
76          // CHECKSTYLE_OFF: LineLength
77          versionMembers.putAll(collectVersionMembers(project.getArtifacts(), dependencies, " (dependency)"));
78          versionMembers.putAll(collectVersionMembers(project.getPluginArtifacts(), allBuildPlugins, " (buildPlugin)"));
79          versionMembers.putAll(collectVersionMembers(project.getReportArtifacts(), allReportPlugins, " (reportPlugin)"));
80          // CHECKSTYLE_ON: LineLength
81  
82          if (versionMembers.size() > 1) {
83              StringBuilder builder = new StringBuilder("Found entries with different versions" + System.lineSeparator());
84              for (Map.Entry<String, List<String>> entry : versionMembers.entrySet()) {
85                  builder.append("Entries with version ").append(entry.getKey()).append(System.lineSeparator());
86                  for (String conflictId : entry.getValue()) {
87                      builder.append("- ").append(conflictId).append(System.lineSeparator());
88                  }
89              }
90              throw new EnforcerRuleException(builder.toString());
91          }
92  
93          if (sameModuleVersions) {
94              MavenProject topLevelProject = session.getTopLevelProject();
95              if (!Objects.equals(topLevelProject.getVersion(), project.getVersion())) {
96                  throw new EnforcerRuleException("Top level project has version " + topLevelProject.getVersion()
97                          + " but current module has different version " + project.getVersion());
98              }
99          }
100     }
101 
102     private Map<String, List<String>> collectVersionMembers(
103             Set<Artifact> artifacts, Collection<String> patterns, String source) {
104         Map<String, List<String>> versionMembers = new LinkedHashMap<>();
105 
106         List<Pattern> regExs = new ArrayList<>();
107         for (String pattern : patterns) {
108             String regex = pattern.replace(".", "\\.")
109                     .replace("*", ".*")
110                     .replace(":", "\\:")
111                     .replace('?', '.');
112 
113             // pattern is groupId[:artifactId[:type[:classifier]]]
114             regExs.add(Pattern.compile(regex + "(\\:.+)?"));
115         }
116 
117         for (Artifact artifact : artifacts) {
118             for (Pattern regEx : regExs) {
119                 if (regEx.matcher(artifact.getDependencyConflictId()).matches()) {
120                     String version = uniqueVersions ? artifact.getVersion() : artifact.getBaseVersion();
121                     versionMembers
122                             .computeIfAbsent(version, unused -> new ArrayList<>())
123                             .add(artifact.getDependencyConflictId() + source);
124                 }
125             }
126         }
127         return versionMembers;
128     }
129 
130     void addDependency(String dependency) {
131         dependencies.add(dependency);
132     }
133 
134     void addPlugin(String plugin) {
135         plugins.add(plugin);
136     }
137 
138     void addBuildPlugin(String buildPlugin) {
139         buildPlugins.add(buildPlugin);
140     }
141 
142     void addReportPlugin(String reportPlugin) {
143         reportPlugins.add(reportPlugin);
144     }
145 
146     @Override
147     public String toString() {
148         return String.format(
149                 "RequireSameVersions[dependencies=%s, buildPlugins=%s, reportPlugins=%s, plugins=%s, uniqueVersions=%b, sameModuleVersions=%b]",
150                 dependencies, buildPlugins, reportPlugins, plugins, uniqueVersions, sameModuleVersions);
151     }
152 }