1 package org.apache.maven.repository.metadata;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.TreeSet;
25
26 import org.apache.maven.artifact.ArtifactScopeEnum;
27 import org.codehaus.plexus.component.annotations.Component;
28 import org.codehaus.plexus.component.annotations.Requirement;
29
30 /**
31 * Default conflict resolver.Implements closer newer first policy by default, but could be configured via plexus
32 *
33 * @author <a href="mailto:oleg@codehaus.org">Oleg Gusakov</a>
34 */
35 @Component( role = GraphConflictResolver.class )
36 public class DefaultGraphConflictResolver
37 implements GraphConflictResolver
38 {
39 /**
40 * artifact, closer to the entry point, is selected
41 */
42 @Requirement( role = GraphConflictResolutionPolicy.class )
43 protected GraphConflictResolutionPolicy policy;
44
45 // -------------------------------------------------------------------------------------
46 // -------------------------------------------------------------------------------------
47 public MetadataGraph resolveConflicts( MetadataGraph graph, ArtifactScopeEnum scope )
48 throws GraphConflictResolutionException
49 {
50 if ( policy == null )
51 {
52 throw new GraphConflictResolutionException( "no GraphConflictResolutionPolicy injected" );
53 }
54
55 if ( graph == null )
56 {
57 return null;
58 }
59
60 final MetadataGraphVertex entry = graph.getEntry();
61 if ( entry == null )
62 {
63 return null;
64 }
65
66 if ( graph.isEmpty() )
67 {
68 throw new GraphConflictResolutionException( "graph with an entry, but not vertices do not exist" );
69 }
70
71 if ( graph.isEmptyEdges() )
72 {
73 return null; // no edges - nothing to worry about
74 }
75
76 final TreeSet<MetadataGraphVertex> vertices = graph.getVertices();
77
78 try
79 {
80 // edge case - single vertex graph
81 if ( vertices.size() == 1 )
82 {
83 return new MetadataGraph( entry );
84 }
85
86 final ArtifactScopeEnum requestedScope = ArtifactScopeEnum.checkScope( scope );
87
88 MetadataGraph res = new MetadataGraph( vertices.size() );
89 res.setVersionedVertices( false );
90 res.setScopedVertices( false );
91
92 MetadataGraphVertex resEntry = res.addVertex( entry.getMd() );
93 res.setEntry( resEntry );
94
95 res.setScope( requestedScope );
96
97 for ( MetadataGraphVertex v : vertices )
98 {
99 final List<MetadataGraphEdge> ins = graph.getIncidentEdges( v );
100 final MetadataGraphEdge edge = cleanEdges( v, ins, requestedScope );
101
102 if ( edge == null )
103 { // no edges - don't need this vertex anymore
104 if ( entry.equals( v ) )
105 { // unless it's an entry point.
106 // currently processing the entry point - it should not have any entry incident edges
107 res.getEntry().getMd().setWhy( "This is a graph entry point. No links." );
108 }
109 else
110 {
111 // System.out.println("--->"+v.getMd().toDomainString()
112 // +" has been terminated on this entry set\n-------------------\n"
113 // +ins
114 // +"\n-------------------\n"
115 // );
116 }
117 }
118 else
119 {
120 // System.out.println("+++>"+v.getMd().toDomainString()+" still has "+edge.toString() );
121 // fill in domain md with actual version data
122 ArtifactMetadata md = v.getMd();
123 ArtifactMetadata newMd =
124 new ArtifactMetadata( md.getGroupId(), md.getArtifactId(), edge.getVersion(), md.getType(),
125 md.getScopeAsEnum(), md.getClassifier(), edge.getArtifactUri(),
126 edge.getSource() == null ? "" : edge.getSource().getMd().toString(),
127 edge.isResolved(), edge.getTarget() == null ? null
128 : edge.getTarget().getMd().getError() );
129 MetadataGraphVertex newV = res.addVertex( newMd );
130 MetadataGraphVertex sourceV = res.addVertex( edge.getSource().getMd() );
131
132 res.addEdge( sourceV, newV, edge );
133 }
134 }
135 // System.err.println("Original graph("+graph.getVertices().size()+"):\n"+graph.toString());
136 // System.err.println("Cleaned("+requestedScope+") graph("+res.getVertices().size()+"):\n"+res.toString());
137 // System.err.println("Linked("+requestedScope+")
138 // subgraph("+linkedRes.getVertices().size()+"):\n"+linkedRes.toString());
139 return findLinkedSubgraph( res );
140 }
141 catch ( MetadataResolutionException e )
142 {
143 throw new GraphConflictResolutionException( e );
144 }
145 }
146
147 // -------------------------------------------------------------------------------------
148 private MetadataGraph findLinkedSubgraph( MetadataGraph g )
149 {
150 if ( g.getVertices().size() == 1 )
151 {
152 return g;
153 }
154
155 List<MetadataGraphVertex> visited = new ArrayList<>( g.getVertices().size() );
156 visit( g.getEntry(), visited, g );
157
158 List<MetadataGraphVertex> dropList = new ArrayList<>( g.getVertices().size() );
159
160 // collect drop list
161 for ( MetadataGraphVertex v : g.getVertices() )
162 {
163 if ( !visited.contains( v ) )
164 {
165 dropList.add( v );
166 }
167 }
168
169 if ( dropList.size() < 1 )
170 {
171 return g;
172 }
173
174 // now - drop vertices
175 TreeSet<MetadataGraphVertex> vertices = g.getVertices();
176 for ( MetadataGraphVertex v : dropList )
177 {
178 vertices.remove( v );
179 }
180
181 return g;
182 }
183
184 // -------------------------------------------------------------------------------------
185 private void visit( MetadataGraphVertex from, List<MetadataGraphVertex> visited, MetadataGraph graph )
186 {
187 if ( visited.contains( from ) )
188 {
189 return;
190 }
191
192 visited.add( from );
193
194 List<MetadataGraphEdge> exitList = graph.getExcidentEdges( from );
195 // String s = "|---> "+from.getMd().toString()+" - "+(exitList == null ? -1 : exitList.size()) + " exit links";
196 if ( exitList != null && exitList.size() > 0 )
197 {
198 for ( MetadataGraphEdge e : graph.getExcidentEdges( from ) )
199 {
200 visit( e.getTarget(), visited, graph );
201 }
202 }
203 }
204
205 // -------------------------------------------------------------------------------------
206 private MetadataGraphEdge cleanEdges( MetadataGraphVertex v, List<MetadataGraphEdge> edges,
207 ArtifactScopeEnum scope )
208 {
209 if ( edges == null || edges.isEmpty() )
210 {
211 return null;
212 }
213
214 if ( edges.size() == 1 )
215 {
216 MetadataGraphEdge e = edges.get( 0 );
217 if ( scope.encloses( e.getScope() ) )
218 {
219 return e;
220 }
221
222 return null;
223 }
224
225 MetadataGraphEdge res = null;
226
227 for ( MetadataGraphEdge e : edges )
228 {
229 if ( !scope.encloses( e.getScope() ) )
230 {
231 continue;
232 }
233
234 if ( res == null )
235 {
236 res = e;
237 }
238 else
239 {
240 res = policy.apply( e, res );
241 }
242 }
243
244 return res;
245 }
246 // -------------------------------------------------------------------------------------
247 // -------------------------------------------------------------------------------------
248 }