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.FileNotFoundException; 023import java.io.FileReader; 024import java.io.IOException; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 032import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; 033import org.apache.maven.model.Dependency; 034import org.apache.maven.model.Model; 035import org.apache.maven.model.Profile; 036import org.apache.maven.model.io.xpp3.MavenXpp3Reader; 037import org.apache.maven.project.MavenProject; 038import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 039import org.codehaus.plexus.util.IOUtil; 040import org.codehaus.plexus.util.xml.pull.XmlPullParserException; 041 042/** 043 * Since Maven 3 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique. Early versions of Maven 044 * 3 already warn, this rule can force to break a build for this reason. 045 * 046 * @author Robert Scholte 047 * @since 1.3 048 */ 049public class BanDuplicatePomDependencyVersions 050 extends AbstractNonCacheableEnforcerRule 051{ 052 053 public void execute( EnforcerRuleHelper helper ) 054 throws EnforcerRuleException 055 { 056 // get the project 057 MavenProject project; 058 try 059 { 060 project = (MavenProject) helper.evaluate( "${project}" ); 061 } 062 catch ( ExpressionEvaluationException eee ) 063 { 064 throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee ); 065 } 066 067 // re-read model, because M3 uses optimized model 068 MavenXpp3Reader modelReader = new MavenXpp3Reader(); 069 FileReader pomReader = null; 070 Model model; 071 try 072 { 073 pomReader = new FileReader( project.getFile() ); 074 075 model = modelReader.read( pomReader ); 076 } 077 catch ( FileNotFoundException e ) 078 { 079 throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", e ); 080 } 081 catch ( IOException e ) 082 { 083 throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", e ); 084 } 085 catch ( XmlPullParserException e ) 086 { 087 throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", e ); 088 } 089 finally 090 { 091 IOUtil.close( pomReader ); 092 } 093 094 // @todo reuse ModelValidator when possible 095 096 // Object modelValidator = null; 097 // try 098 // { 099 // modelValidator = helper.getComponent( "org.apache.maven.model.validation.ModelValidator" ); 100 // } 101 // catch ( ComponentLookupException e1 ) 102 // { 103 // // noop 104 // } 105 106 // if( modelValidator == null ) 107 // { 108 maven2Validation( helper, model ); 109 // } 110 // else 111 // { 112 // } 113 } 114 115 private void maven2Validation( EnforcerRuleHelper helper, Model model ) 116 throws EnforcerRuleException 117 { 118 List<Dependency> dependencies = model.getDependencies(); 119 Map<String, Integer> duplicateDependencies = validateDependencies( dependencies ); 120 121 int duplicates = duplicateDependencies.size(); 122 123 StringBuilder summary = new StringBuilder(); 124 messageBuilder( duplicateDependencies, "dependencies.dependency", summary ); 125 126 if ( model.getDependencyManagement() != null ) 127 { 128 List<Dependency> managementDependencies = model.getDependencies(); 129 Map<String, Integer> duplicateManagementDependencies = validateDependencies( managementDependencies ); 130 duplicates += duplicateManagementDependencies.size(); 131 132 messageBuilder( duplicateManagementDependencies, "dependencyManagement.dependencies.dependency", summary ); 133 } 134 135 List<Profile> profiles = model.getProfiles(); 136 for ( Profile profile : profiles ) 137 { 138 List<Dependency> profileDependencies = profile.getDependencies(); 139 140 Map<String, Integer> duplicateProfileDependencies = validateDependencies( profileDependencies ); 141 142 duplicates += duplicateProfileDependencies.size(); 143 144 messageBuilder( duplicateProfileDependencies, "profiles.profile[" + profile.getId() 145 + "].dependencies.dependency", summary ); 146 147 if ( model.getDependencyManagement() != null ) 148 { 149 List<Dependency> profileManagementDependencies = profile.getDependencies(); 150 151 Map<String, Integer> duplicateProfileManagementDependencies = 152 validateDependencies( profileManagementDependencies ); 153 154 duplicates += duplicateProfileManagementDependencies.size(); 155 156 messageBuilder( duplicateProfileManagementDependencies, "profiles.profile[" + profile.getId() 157 + "].dependencyManagement.dependencies.dependency", summary ); 158 } 159 } 160 161 if ( summary.length() > 0 ) 162 { 163 StringBuilder message = new StringBuilder(); 164 message.append( "Found " ) 165 .append( duplicates ) 166 .append( " duplicate dependency " ); 167 message.append( duplicateDependencies.size() == 1 ? "declaration" : "declarations" ) 168 .append( " in this project:\n" ); 169 message.append( summary ); 170 throw new EnforcerRuleException( message.toString() ); 171 } 172 } 173 174 private void messageBuilder( Map<String, Integer> duplicateDependencies, String prefix, StringBuilder message ) 175 { 176 if ( !duplicateDependencies.isEmpty() ) 177 { 178 for ( Map.Entry<String, Integer> entry : duplicateDependencies.entrySet() ) 179 { 180 message.append( " - " ) 181 .append( prefix ) 182 .append( '[' ) 183 .append( entry.getKey() ) 184 .append( "] ( " ) 185 .append( entry.getValue() ) 186 .append( " times )\n" ); 187 } 188 } 189 } 190 191 private Map<String, Integer> validateDependencies( List<Dependency> dependencies ) 192 throws EnforcerRuleException 193 { 194 Map<String, Integer> duplicateDeps = new HashMap<String, Integer>(); 195 Set<String> deps = new HashSet<String>(); 196 for ( Dependency dependency : dependencies ) 197 { 198 String key = dependency.getManagementKey(); 199 200 if ( deps.contains( key ) ) 201 { 202 int times = 1; 203 if ( duplicateDeps.containsKey( key ) ) 204 { 205 times = duplicateDeps.get( key ); 206 } 207 duplicateDeps.put( key, times + 1 ); 208 } 209 else 210 { 211 deps.add( key ); 212 } 213 } 214 return duplicateDeps; 215 } 216 217}