package org.apache.maven.plugins.enforcer; | |
/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import org.apache.maven.enforcer.rule.api.EnforcerRuleException; | |
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; | |
import org.apache.maven.model.Dependency; | |
import org.apache.maven.model.Model; | |
import org.apache.maven.model.Profile; | |
import org.apache.maven.model.io.xpp3.MavenXpp3Reader; | |
import org.apache.maven.project.MavenProject; | |
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; | |
import org.codehaus.plexus.util.xml.pull.XmlPullParserException; | |
/** | |
* Since Maven 3 'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique. Early versions of Maven | |
* 3 already warn, this rule can force to break a build for this reason. | |
* | |
* @author Robert Scholte | |
* @since 1.3 | |
*/ | |
public class BanDuplicatePomDependencyVersions | |
extends AbstractNonCacheableEnforcerRule | |
{ | |
@Override | |
public void execute( EnforcerRuleHelper helper ) | |
throws EnforcerRuleException | |
{ | |
// get the project | |
MavenProject project; | |
try | |
{ | |
project = (MavenProject) helper.evaluate( "${project}" ); | |
} | |
catch ( ExpressionEvaluationException eee ) | |
{ | |
throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee ); | |
} | |
// re-read model, because M3 uses optimized model | |
MavenXpp3Reader modelReader = new MavenXpp3Reader(); | |
Model model; | |
try ( FileInputStream pomInputStream = new FileInputStream( project.getFile() ) ) | |
{ | |
model = modelReader.read( pomInputStream, false ); | |
} | |
catch ( IOException | XmlPullParserException e ) | |
{ | |
throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", e ); | |
} | |
// @todo reuse ModelValidator when possible | |
// Object modelValidator = null; | |
// try | |
// { | |
// modelValidator = helper.getComponent( "org.apache.maven.model.validation.ModelValidator" ); | |
// } | |
// catch ( ComponentLookupException e1 ) | |
// { | |
// // noop | |
// } | |
// if( modelValidator == null ) | |
// { | |
maven2Validation( helper, model ); | |
// } | |
// else | |
// { | |
// } | |
} | |
private void maven2Validation( EnforcerRuleHelper helper, Model model ) | |
throws EnforcerRuleException | |
{ | |
List<Dependency> dependencies = model.getDependencies(); | |
Map<String, Integer> duplicateDependencies = validateDependencies( dependencies ); | |
int duplicates = duplicateDependencies.size(); | |
StringBuilder summary = new StringBuilder(); | |
messageBuilder( duplicateDependencies, "dependencies.dependency", summary ); | |
if ( model.getDependencyManagement() != null ) | |
{ | |
List<Dependency> managementDependencies = model.getDependencyManagement().getDependencies(); | |
Map<String, Integer> duplicateManagementDependencies = validateDependencies( managementDependencies ); | |
duplicates += duplicateManagementDependencies.size(); | |
messageBuilder( duplicateManagementDependencies, "dependencyManagement.dependencies.dependency", summary ); | |
} | |
List<Profile> profiles = model.getProfiles(); | |
for ( Profile profile : profiles ) | |
{ | |
List<Dependency> profileDependencies = profile.getDependencies(); | |
Map<String, Integer> duplicateProfileDependencies = validateDependencies( profileDependencies ); | |
duplicates += duplicateProfileDependencies.size(); | |
messageBuilder( duplicateProfileDependencies, "profiles.profile[" + profile.getId() | |
+ "].dependencies.dependency", summary ); | |
if ( profile.getDependencyManagement() != null ) | |
{ | |
List<Dependency> profileManagementDependencies = profile.getDependencyManagement().getDependencies(); | |
Map<String, Integer> duplicateProfileManagementDependencies = | |
validateDependencies( profileManagementDependencies ); | |
duplicates += duplicateProfileManagementDependencies.size(); | |
messageBuilder( duplicateProfileManagementDependencies, "profiles.profile[" + profile.getId() | |
+ "].dependencyManagement.dependencies.dependency", summary ); | |
} | |
} | |
if ( summary.length() > 0 ) | |
{ | |
StringBuilder message = new StringBuilder(); | |
message.append( "Found " ) | |
.append( duplicates ) | |
.append( " duplicate dependency " ); | |
message.append( duplicateDependencies.size() == 1 ? "declaration" : "declarations" ) | |
.append( " in this project:" + System.lineSeparator() ); | |
message.append( summary ); | |
throw new EnforcerRuleException( message.toString() ); | |
} | |
} | |
private void messageBuilder( Map<String, Integer> duplicateDependencies, String prefix, StringBuilder message ) | |
{ | |
if ( !duplicateDependencies.isEmpty() ) | |
{ | |
for ( Map.Entry<String, Integer> entry : duplicateDependencies.entrySet() ) | |
{ | |
message.append( " - " ) | |
.append( prefix ) | |
.append( '[' ) | |
.append( entry.getKey() ) | |
.append( "] ( " ) | |
.append( entry.getValue() ) | |
.append( " times )" + System.lineSeparator() ); | |
} | |
} | |
} | |
private Map<String, Integer> validateDependencies( List<Dependency> dependencies ) | |
throws EnforcerRuleException | |
{ | |
Map<String, Integer> duplicateDeps = new HashMap<>(); | |
Set<String> deps = new HashSet<>(); | |
for ( Dependency dependency : dependencies ) | |
{ | |
String key = dependency.getManagementKey(); | |
if ( deps.contains( key ) ) | |
{ | |
int times = 1; | |
if ( duplicateDeps.containsKey( key ) ) | |
{ | |
times = duplicateDeps.get( key ); | |
} | |
duplicateDeps.put( key, times + 1 ); | |
} | |
else | |
{ | |
deps.add( key ); | |
} | |
} | |
return duplicateDeps; | |
} | |
} |