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 }