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