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.model.composition;
20  
21  import javax.inject.Named;
22  import javax.inject.Singleton;
23  
24  import java.util.Collection;
25  import java.util.HashSet;
26  import java.util.Iterator;
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  
33  import org.apache.maven.api.model.Dependency;
34  import org.apache.maven.api.model.DependencyManagement;
35  import org.apache.maven.api.model.Exclusion;
36  import org.apache.maven.api.model.Model;
37  import org.apache.maven.model.building.ModelBuildingRequest;
38  import org.apache.maven.model.building.ModelProblem;
39  import org.apache.maven.model.building.ModelProblemCollector;
40  import org.apache.maven.model.building.ModelProblemCollectorRequest;
41  
42  /**
43   * Handles the import of dependency management from other models into the target model.
44   *
45   */
46  @Named
47  @Singleton
48  public class DefaultDependencyManagementImporter implements DependencyManagementImporter {
49  
50      @Override
51      public Model importManagement(
52              Model target,
53              List<? extends DependencyManagement> sources,
54              ModelBuildingRequest request,
55              ModelProblemCollector problems) {
56          if (sources != null && !sources.isEmpty()) {
57              Map<String, Dependency> dependencies = new LinkedHashMap<>();
58  
59              DependencyManagement depMgmt = target.getDependencyManagement();
60  
61              if (depMgmt != null) {
62                  for (Dependency dependency : depMgmt.getDependencies()) {
63                      dependencies.put(dependency.getManagementKey(), dependency);
64                  }
65              } else {
66                  depMgmt = DependencyManagement.newInstance();
67              }
68  
69              Set<String> directDependencies = new HashSet<>(dependencies.keySet());
70  
71              for (DependencyManagement source : sources) {
72                  for (Dependency dependency : source.getDependencies()) {
73                      String key = dependency.getManagementKey();
74                      Dependency present = dependencies.putIfAbsent(key, dependency);
75                      if (present != null && !equals(dependency, present) && !directDependencies.contains(key)) {
76                          // TODO: https://issues.apache.org/jira/browse/MNG-8004
77                          problems.add(new ModelProblemCollectorRequest(
78                                          ModelProblem.Severity.WARNING, ModelProblem.Version.V40)
79                                  .setMessage("Ignored POM import for: " + toString(dependency) + " as already imported "
80                                          + toString(present) + ". Add the conflicting managed dependency directly "
81                                          + "to the dependencyManagement section of the POM."));
82                      }
83                  }
84              }
85  
86              return target.withDependencyManagement(depMgmt.withDependencies(dependencies.values()));
87          }
88          return target;
89      }
90  
91      private String toString(Dependency dependency) {
92          StringBuilder stringBuilder = new StringBuilder();
93          stringBuilder
94                  .append(dependency.getGroupId())
95                  .append(":")
96                  .append(dependency.getArtifactId())
97                  .append(":")
98                  .append(dependency.getType());
99          if (dependency.getClassifier() != null && !dependency.getClassifier().isEmpty()) {
100             stringBuilder.append(":").append(dependency.getClassifier());
101         }
102         stringBuilder
103                 .append(":")
104                 .append(dependency.getVersion())
105                 .append("@")
106                 .append(dependency.getScope() == null ? "compile" : dependency.getScope());
107         if (dependency.isOptional()) {
108             stringBuilder.append("[optional]");
109         }
110         if (!dependency.getExclusions().isEmpty()) {
111             stringBuilder.append("[").append(dependency.getExclusions().size()).append(" exclusions]");
112         }
113         return stringBuilder.toString();
114     }
115 
116     private boolean equals(Dependency d1, Dependency d2) {
117         return Objects.equals(d1.getGroupId(), d2.getGroupId())
118                 && Objects.equals(d1.getArtifactId(), d2.getArtifactId())
119                 && Objects.equals(d1.getVersion(), d2.getVersion())
120                 && Objects.equals(d1.getType(), d2.getType())
121                 && Objects.equals(d1.getClassifier(), d2.getClassifier())
122                 && Objects.equals(d1.getScope(), d2.getScope())
123                 && Objects.equals(d1.getSystemPath(), d2.getSystemPath())
124                 && Objects.equals(d1.getOptional(), d2.getOptional())
125                 && equals(d1.getExclusions(), d2.getExclusions());
126     }
127 
128     private boolean equals(Collection<Exclusion> ce1, Collection<Exclusion> ce2) {
129         if (ce1.size() == ce2.size()) {
130             Iterator<Exclusion> i1 = ce1.iterator();
131             Iterator<Exclusion> i2 = ce2.iterator();
132             while (i1.hasNext() && i2.hasNext()) {
133                 if (!equals(i1.next(), i2.next())) {
134                     return false;
135                 }
136             }
137             return !i1.hasNext() && !i2.hasNext();
138         }
139         return false;
140     }
141 
142     private boolean equals(Exclusion e1, Exclusion e2) {
143         return Objects.equals(e1.getGroupId(), e2.getGroupId())
144                 && Objects.equals(e1.getArtifactId(), e2.getArtifactId());
145     }
146 }