001package org.apache.maven.plugins.enforcer; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.FileInputStream; 023import java.io.IOException; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 031import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; 032import org.apache.maven.model.Dependency; 033import org.apache.maven.model.Model; 034import org.apache.maven.model.Profile; 035import org.apache.maven.model.io.xpp3.MavenXpp3Reader; 036import org.apache.maven.project.MavenProject; 037import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 038import org.codehaus.plexus.util.xml.pull.XmlPullParserException; 039 040/** 041 * Since Maven 3 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique. Early versions of Maven 042 * 3 already warn, this rule can force to break a build for this reason. 043 * 044 * @author Robert Scholte 045 * @since 1.3 046 */ 047public class BanDuplicatePomDependencyVersions 048 extends AbstractNonCacheableEnforcerRule 049{ 050 @Override 051 public void execute( EnforcerRuleHelper helper ) 052 throws EnforcerRuleException 053 { 054 // get the project 055 MavenProject project; 056 try 057 { 058 project = (MavenProject) helper.evaluate( "${project}" ); 059 } 060 catch ( ExpressionEvaluationException eee ) 061 { 062 throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee ); 063 } 064 065 // re-read model, because M3 uses optimized model 066 MavenXpp3Reader modelReader = new MavenXpp3Reader(); 067 068 Model model; 069 try ( FileInputStream pomInputStream = new FileInputStream( project.getFile() ) ) 070 { 071 model = modelReader.read( pomInputStream, false ); 072 } 073 catch ( IOException | XmlPullParserException e ) 074 { 075 throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", e ); 076 } 077 078 // @todo reuse ModelValidator when possible 079 080 // Object modelValidator = null; 081 // try 082 // { 083 // modelValidator = helper.getComponent( "org.apache.maven.model.validation.ModelValidator" ); 084 // } 085 // catch ( ComponentLookupException e1 ) 086 // { 087 // // noop 088 // } 089 090 // if( modelValidator == null ) 091 // { 092 maven2Validation( helper, model ); 093 // } 094 // else 095 // { 096 // } 097 } 098 099 private void maven2Validation( EnforcerRuleHelper helper, Model model ) 100 throws EnforcerRuleException 101 { 102 List<Dependency> dependencies = model.getDependencies(); 103 Map<String, Integer> duplicateDependencies = validateDependencies( dependencies ); 104 105 int duplicates = duplicateDependencies.size(); 106 107 StringBuilder summary = new StringBuilder(); 108 messageBuilder( duplicateDependencies, "dependencies.dependency", summary ); 109 110 if ( model.getDependencyManagement() != null ) 111 { 112 List<Dependency> managementDependencies = model.getDependencyManagement().getDependencies(); 113 Map<String, Integer> duplicateManagementDependencies = validateDependencies( managementDependencies ); 114 duplicates += duplicateManagementDependencies.size(); 115 116 messageBuilder( duplicateManagementDependencies, "dependencyManagement.dependencies.dependency", summary ); 117 } 118 119 List<Profile> profiles = model.getProfiles(); 120 for ( Profile profile : profiles ) 121 { 122 List<Dependency> profileDependencies = profile.getDependencies(); 123 124 Map<String, Integer> duplicateProfileDependencies = validateDependencies( profileDependencies ); 125 126 duplicates += duplicateProfileDependencies.size(); 127 128 messageBuilder( duplicateProfileDependencies, "profiles.profile[" + profile.getId() 129 + "].dependencies.dependency", summary ); 130 131 if ( profile.getDependencyManagement() != null ) 132 { 133 List<Dependency> profileManagementDependencies = profile.getDependencyManagement().getDependencies(); 134 135 Map<String, Integer> duplicateProfileManagementDependencies = 136 validateDependencies( profileManagementDependencies ); 137 138 duplicates += duplicateProfileManagementDependencies.size(); 139 140 messageBuilder( duplicateProfileManagementDependencies, "profiles.profile[" + profile.getId() 141 + "].dependencyManagement.dependencies.dependency", summary ); 142 } 143 } 144 145 if ( summary.length() > 0 ) 146 { 147 StringBuilder message = new StringBuilder(); 148 message.append( "Found " ) 149 .append( duplicates ) 150 .append( " duplicate dependency " ); 151 message.append( duplicateDependencies.size() == 1 ? "declaration" : "declarations" ) 152 .append( " in this project:" + System.lineSeparator() ); 153 message.append( summary ); 154 throw new EnforcerRuleException( message.toString() ); 155 } 156 } 157 158 private void messageBuilder( Map<String, Integer> duplicateDependencies, String prefix, StringBuilder message ) 159 { 160 if ( !duplicateDependencies.isEmpty() ) 161 { 162 for ( Map.Entry<String, Integer> entry : duplicateDependencies.entrySet() ) 163 { 164 message.append( " - " ) 165 .append( prefix ) 166 .append( '[' ) 167 .append( entry.getKey() ) 168 .append( "] ( " ) 169 .append( entry.getValue() ) 170 .append( " times )" + System.lineSeparator() ); 171 } 172 } 173 } 174 175 private Map<String, Integer> validateDependencies( List<Dependency> dependencies ) 176 throws EnforcerRuleException 177 { 178 Map<String, Integer> duplicateDeps = new HashMap<>(); 179 Set<String> deps = new HashSet<>(); 180 for ( Dependency dependency : dependencies ) 181 { 182 String key = dependency.getManagementKey(); 183 184 if ( deps.contains( key ) ) 185 { 186 int times = 1; 187 if ( duplicateDeps.containsKey( key ) ) 188 { 189 times = duplicateDeps.get( key ); 190 } 191 duplicateDeps.put( key, times + 1 ); 192 } 193 else 194 { 195 deps.add( key ); 196 } 197 } 198 return duplicateDeps; 199 } 200 201}