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 }