View Javadoc
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 }