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.repository.metadata;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.TreeSet;
24  import org.apache.maven.artifact.ArtifactScopeEnum;
25  import org.codehaus.plexus.component.annotations.Component;
26  import org.codehaus.plexus.component.annotations.Requirement;
27  
28  /**
29   * Default conflict resolver.Implements closer newer first policy by default, but could be configured via plexus
30   *
31   * @author <a href="mailto:oleg@codehaus.org">Oleg Gusakov</a>
32   */
33  @Component(role = GraphConflictResolver.class)
34  public class DefaultGraphConflictResolver implements GraphConflictResolver {
35      /**
36       * artifact, closer to the entry point, is selected
37       */
38      @Requirement(role = GraphConflictResolutionPolicy.class)
39      protected GraphConflictResolutionPolicy policy;
40  
41      // -------------------------------------------------------------------------------------
42      // -------------------------------------------------------------------------------------
43      public MetadataGraph resolveConflicts(MetadataGraph graph, ArtifactScopeEnum scope)
44              throws GraphConflictResolutionException {
45          if (policy == null) {
46              throw new GraphConflictResolutionException("no GraphConflictResolutionPolicy injected");
47          }
48  
49          if (graph == null) {
50              return null;
51          }
52  
53          final MetadataGraphVertex entry = graph.getEntry();
54          if (entry == null) {
55              return null;
56          }
57  
58          if (graph.isEmpty()) {
59              throw new GraphConflictResolutionException("graph with an entry, but not vertices do not exist");
60          }
61  
62          if (graph.isEmptyEdges()) {
63              return null; // no edges - nothing to worry about
64          }
65  
66          final TreeSet<MetadataGraphVertex> vertices = graph.getVertices();
67  
68          try {
69              // edge case - single vertex graph
70              if (vertices.size() == 1) {
71                  return new MetadataGraph(entry);
72              }
73  
74              final ArtifactScopeEnum requestedScope = ArtifactScopeEnum.checkScope(scope);
75  
76              MetadataGraph res = new MetadataGraph(vertices.size());
77              res.setVersionedVertices(false);
78              res.setScopedVertices(false);
79  
80              MetadataGraphVertex resEntry = res.addVertex(entry.getMd());
81              res.setEntry(resEntry);
82  
83              res.setScope(requestedScope);
84  
85              for (MetadataGraphVertex v : vertices) {
86                  final List<MetadataGraphEdge> ins = graph.getIncidentEdges(v);
87                  final MetadataGraphEdge edge = cleanEdges(v, ins, requestedScope);
88  
89                  if (edge == null) { // no edges - don't need this vertex anymore
90                      if (entry.equals(v)) { // unless it's an entry point.
91                          // currently processing the entry point - it should not have any entry incident edges
92                          res.getEntry().getMd().setWhy("This is a graph entry point. No links.");
93                      } else {
94                          // System.out.println("--->"+v.getMd().toDomainString()
95                          // +" has been terminated on this entry set\n-------------------\n"
96                          // +ins
97                          // +"\n-------------------\n"
98                          // );
99                      }
100                 } else {
101                     // System.out.println("+++>"+v.getMd().toDomainString()+" still has "+edge.toString() );
102                     // fill in domain md with actual version data
103                     ArtifactMetadata md = v.getMd();
104                     ArtifactMetadata newMd = new ArtifactMetadata(
105                             md.getGroupId(),
106                             md.getArtifactId(),
107                             edge.getVersion(),
108                             md.getType(),
109                             md.getScopeAsEnum(),
110                             md.getClassifier(),
111                             edge.getArtifactUri(),
112                             edge.getSource() == null
113                                     ? ""
114                                     : edge.getSource().getMd().toString(),
115                             edge.isResolved(),
116                             edge.getTarget() == null
117                                     ? null
118                                     : edge.getTarget().getMd().getError());
119                     MetadataGraphVertex newV = res.addVertex(newMd);
120                     MetadataGraphVertex sourceV = res.addVertex(edge.getSource().getMd());
121 
122                     res.addEdge(sourceV, newV, edge);
123                 }
124             }
125             // System.err.println("Original graph("+graph.getVertices().size()+"):\n"+graph.toString());
126             // System.err.println("Cleaned("+requestedScope+") graph("+res.getVertices().size()+"):\n"+res.toString());
127             // System.err.println("Linked("+requestedScope+")
128             // subgraph("+linkedRes.getVertices().size()+"):\n"+linkedRes.toString());
129             return findLinkedSubgraph(res);
130         } catch (MetadataResolutionException e) {
131             throw new GraphConflictResolutionException(e);
132         }
133     }
134 
135     // -------------------------------------------------------------------------------------
136     private MetadataGraph findLinkedSubgraph(MetadataGraph g) {
137         if (g.getVertices().size() == 1) {
138             return g;
139         }
140 
141         List<MetadataGraphVertex> visited = new ArrayList<>(g.getVertices().size());
142         visit(g.getEntry(), visited, g);
143 
144         List<MetadataGraphVertex> dropList = new ArrayList<>(g.getVertices().size());
145 
146         // collect drop list
147         for (MetadataGraphVertex v : g.getVertices()) {
148             if (!visited.contains(v)) {
149                 dropList.add(v);
150             }
151         }
152 
153         if (dropList.size() < 1) {
154             return g;
155         }
156 
157         // now - drop vertices
158         TreeSet<MetadataGraphVertex> vertices = g.getVertices();
159         for (MetadataGraphVertex v : dropList) {
160             vertices.remove(v);
161         }
162 
163         return g;
164     }
165 
166     // -------------------------------------------------------------------------------------
167     private void visit(MetadataGraphVertex from, List<MetadataGraphVertex> visited, MetadataGraph graph) {
168         if (visited.contains(from)) {
169             return;
170         }
171 
172         visited.add(from);
173 
174         List<MetadataGraphEdge> exitList = graph.getExcidentEdges(from);
175         // String s = "|---> "+from.getMd().toString()+" - "+(exitList == null ? -1 : exitList.size()) + " exit links";
176         if (exitList != null && exitList.size() > 0) {
177             for (MetadataGraphEdge e : graph.getExcidentEdges(from)) {
178                 visit(e.getTarget(), visited, graph);
179             }
180         }
181     }
182 
183     // -------------------------------------------------------------------------------------
184     private MetadataGraphEdge cleanEdges(
185             MetadataGraphVertex v, List<MetadataGraphEdge> edges, ArtifactScopeEnum scope) {
186         if (edges == null || edges.isEmpty()) {
187             return null;
188         }
189 
190         if (edges.size() == 1) {
191             MetadataGraphEdge e = edges.get(0);
192             if (scope.encloses(e.getScope())) {
193                 return e;
194             }
195 
196             return null;
197         }
198 
199         MetadataGraphEdge res = null;
200 
201         for (MetadataGraphEdge e : edges) {
202             if (!scope.encloses(e.getScope())) {
203                 continue;
204             }
205 
206             if (res == null) {
207                 res = e;
208             } else {
209                 res = policy.apply(e, res);
210             }
211         }
212 
213         return res;
214     }
215     // -------------------------------------------------------------------------------------
216     // -------------------------------------------------------------------------------------
217 }