View Javadoc
1   package org.apache.maven.repository.internal;
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.LinkedHashSet;
23  import java.util.Map;
24  import java.util.Objects;
25  import java.util.Properties;
26  import java.util.Set;
27  
28  import javax.inject.Inject;
29  import javax.inject.Named;
30  import javax.inject.Singleton;
31  
32  import org.apache.maven.model.DistributionManagement;
33  import org.apache.maven.model.Model;
34  import org.apache.maven.model.Relocation;
35  import org.apache.maven.model.building.DefaultModelBuilderFactory;
36  import org.apache.maven.model.building.DefaultModelBuildingRequest;
37  import org.apache.maven.model.building.FileModelSource;
38  import org.apache.maven.model.building.ModelBuilder;
39  import org.apache.maven.model.building.ModelBuildingException;
40  import org.apache.maven.model.building.ModelBuildingRequest;
41  import org.apache.maven.model.building.ModelProblem;
42  import org.apache.maven.model.resolution.UnresolvableModelException;
43  import org.eclipse.aether.RepositoryEvent;
44  import org.eclipse.aether.RepositoryEvent.EventType;
45  import org.eclipse.aether.RepositoryException;
46  import org.eclipse.aether.RepositorySystemSession;
47  import org.eclipse.aether.RequestTrace;
48  import org.eclipse.aether.artifact.Artifact;
49  import org.eclipse.aether.impl.ArtifactDescriptorReader;
50  import org.eclipse.aether.impl.ArtifactResolver;
51  import org.eclipse.aether.impl.RemoteRepositoryManager;
52  import org.eclipse.aether.impl.RepositoryEventDispatcher;
53  import org.eclipse.aether.impl.VersionRangeResolver;
54  import org.eclipse.aether.impl.VersionResolver;
55  import org.eclipse.aether.repository.WorkspaceReader;
56  import org.eclipse.aether.repository.WorkspaceRepository;
57  import org.eclipse.aether.resolution.ArtifactDescriptorException;
58  import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
59  import org.eclipse.aether.resolution.ArtifactDescriptorPolicyRequest;
60  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
61  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
62  import org.eclipse.aether.resolution.ArtifactRequest;
63  import org.eclipse.aether.resolution.ArtifactResolutionException;
64  import org.eclipse.aether.resolution.ArtifactResult;
65  import org.eclipse.aether.resolution.VersionRequest;
66  import org.eclipse.aether.resolution.VersionResolutionException;
67  import org.eclipse.aether.resolution.VersionResult;
68  import org.eclipse.aether.spi.locator.Service;
69  import org.eclipse.aether.spi.locator.ServiceLocator;
70  import org.eclipse.aether.transfer.ArtifactNotFoundException;
71  import org.slf4j.Logger;
72  import org.slf4j.LoggerFactory;
73  
74  /**
75   * @author Benjamin Bentmann
76   */
77  @Named
78  @Singleton
79  public class DefaultArtifactDescriptorReader
80      implements ArtifactDescriptorReader, Service
81  {
82      private static final Logger LOGGER = LoggerFactory.getLogger( DefaultArtifactDescriptorReader.class );
83  
84      private RemoteRepositoryManager remoteRepositoryManager;
85  
86      private VersionResolver versionResolver;
87  
88      private VersionRangeResolver versionRangeResolver;
89  
90      private ArtifactResolver artifactResolver;
91  
92      private RepositoryEventDispatcher repositoryEventDispatcher;
93  
94      private ModelBuilder modelBuilder;
95  
96      public DefaultArtifactDescriptorReader()
97      {
98          // enable no-arg constructor
99      }
100 
101     @Inject
102     DefaultArtifactDescriptorReader( RemoteRepositoryManager remoteRepositoryManager, VersionResolver versionResolver,
103                                      VersionRangeResolver versionRangeResolver, ArtifactResolver artifactResolver,
104                                      ModelBuilder modelBuilder, RepositoryEventDispatcher repositoryEventDispatcher )
105     {
106         setRemoteRepositoryManager( remoteRepositoryManager );
107         setVersionResolver( versionResolver );
108         setVersionRangeResolver( versionRangeResolver );
109         setArtifactResolver( artifactResolver );
110         setModelBuilder( modelBuilder );
111         setRepositoryEventDispatcher( repositoryEventDispatcher );
112     }
113 
114     public void initService( ServiceLocator locator )
115     {
116         setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
117         setVersionResolver( locator.getService( VersionResolver.class ) );
118         setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
119         setArtifactResolver( locator.getService( ArtifactResolver.class ) );
120         modelBuilder = locator.getService( ModelBuilder.class );
121         if ( modelBuilder == null )
122         {
123             setModelBuilder( new DefaultModelBuilderFactory().newInstance() );
124         }
125         setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
126     }
127 
128     public DefaultArtifactDescriptorReader setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
129     {
130         this.remoteRepositoryManager = Objects.requireNonNull( remoteRepositoryManager,
131             "remoteRepositoryManager cannot be null" );
132         return this;
133     }
134 
135     public DefaultArtifactDescriptorReader setVersionResolver( VersionResolver versionResolver )
136     {
137         this.versionResolver = Objects.requireNonNull( versionResolver, "versionResolver cannot be null" );
138         return this;
139     }
140 
141     /** @since 3.2.2 */
142     public DefaultArtifactDescriptorReader setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
143     {
144         this.versionRangeResolver =
145             Objects.requireNonNull( versionRangeResolver, "versionRangeResolver cannot be null" );
146         return this;
147     }
148 
149     public DefaultArtifactDescriptorReader setArtifactResolver( ArtifactResolver artifactResolver )
150     {
151         this.artifactResolver = Objects.requireNonNull( artifactResolver, "artifactResolver cannot be null" );
152         return this;
153     }
154 
155     public DefaultArtifactDescriptorReader setRepositoryEventDispatcher(
156         RepositoryEventDispatcher repositoryEventDispatcher )
157     {
158         this.repositoryEventDispatcher = Objects.requireNonNull( repositoryEventDispatcher,
159             "repositoryEventDispatcher cannot be null" );
160         return this;
161     }
162 
163     public DefaultArtifactDescriptorReader setModelBuilder( ModelBuilder modelBuilder )
164     {
165         this.modelBuilder = Objects.requireNonNull( modelBuilder, "modelBuilder cannot be null" );
166         return this;
167     }
168 
169     public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session,
170                                                             ArtifactDescriptorRequest request )
171         throws ArtifactDescriptorException
172     {
173         ArtifactDescriptorResult result = new ArtifactDescriptorResult( request );
174 
175         Model model = loadPom( session, request, result );
176         if ( model != null )
177         {
178             Map<String, Object> config = session.getConfigProperties();
179             ArtifactDescriptorReaderDelegate delegate =
180                 (ArtifactDescriptorReaderDelegate) config.get( ArtifactDescriptorReaderDelegate.class.getName() );
181 
182             if ( delegate == null )
183             {
184                 delegate = new ArtifactDescriptorReaderDelegate();
185             }
186 
187             delegate.populateResult( session, result, model );
188         }
189 
190         return result;
191     }
192 
193     private Model loadPom( RepositorySystemSession session, ArtifactDescriptorRequest request,
194                            ArtifactDescriptorResult result )
195         throws ArtifactDescriptorException
196     {
197         RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
198 
199         Set<String> visited = new LinkedHashSet<>();
200         for ( Artifact a = request.getArtifact();; )
201         {
202             Artifact pomArtifact = ArtifactDescriptorUtils.toPomArtifact( a );
203             try
204             {
205                 VersionRequest versionRequest =
206                     new VersionRequest( a, request.getRepositories(), request.getRequestContext() );
207                 versionRequest.setTrace( trace );
208                 VersionResult versionResult = versionResolver.resolveVersion( session, versionRequest );
209 
210                 a = a.setVersion( versionResult.getVersion() );
211 
212                 versionRequest =
213                     new VersionRequest( pomArtifact, request.getRepositories(), request.getRequestContext() );
214                 versionRequest.setTrace( trace );
215                 versionResult = versionResolver.resolveVersion( session, versionRequest );
216 
217                 pomArtifact = pomArtifact.setVersion( versionResult.getVersion() );
218             }
219             catch ( VersionResolutionException e )
220             {
221                 result.addException( e );
222                 throw new ArtifactDescriptorException( result );
223             }
224 
225             if ( !visited.add( a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getBaseVersion() ) )
226             {
227                 RepositoryException exception =
228                     new RepositoryException( "Artifact relocations form a cycle: " + visited );
229                 invalidDescriptor( session, trace, a, exception );
230                 if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 )
231                 {
232                     return null;
233                 }
234                 result.addException( exception );
235                 throw new ArtifactDescriptorException( result );
236             }
237 
238             ArtifactResult resolveResult;
239             try
240             {
241                 ArtifactRequest resolveRequest =
242                     new ArtifactRequest( pomArtifact, request.getRepositories(), request.getRequestContext() );
243                 resolveRequest.setTrace( trace );
244                 resolveResult = artifactResolver.resolveArtifact( session, resolveRequest );
245                 pomArtifact = resolveResult.getArtifact();
246                 result.setRepository( resolveResult.getRepository() );
247             }
248             catch ( ArtifactResolutionException e )
249             {
250                 if ( e.getCause() instanceof ArtifactNotFoundException )
251                 {
252                     missingDescriptor( session, trace, a, (Exception) e.getCause() );
253                     if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_MISSING ) != 0 )
254                     {
255                         return null;
256                     }
257                 }
258                 result.addException( e );
259                 throw new ArtifactDescriptorException( result );
260             }
261 
262             Model model;
263 
264             // TODO hack: don't rebuild model if it was already loaded during reactor resolution
265             final WorkspaceReader workspace = session.getWorkspaceReader();
266             if ( workspace instanceof MavenWorkspaceReader )
267             {
268                 model = ( (MavenWorkspaceReader) workspace ).findModel( pomArtifact );
269                 if ( model != null )
270                 {
271                     return model;
272                 }
273             }
274 
275             try
276             {
277                 ModelBuildingRequest modelRequest = new DefaultModelBuildingRequest();
278                 modelRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
279                 modelRequest.setProcessPlugins( false );
280                 modelRequest.setTwoPhaseBuilding( false );
281                 modelRequest.setSystemProperties( toProperties( session.getUserProperties(),
282                                                                 session.getSystemProperties() ) );
283                 modelRequest.setModelCache( DefaultModelCache.newInstance( session ) );
284                 modelRequest.setModelResolver( new DefaultModelResolver( session, trace.newChild( modelRequest ),
285                                                                          request.getRequestContext(), artifactResolver,
286                                                                          versionRangeResolver, remoteRepositoryManager,
287                                                                          request.getRepositories() ) );
288                 if ( resolveResult.getRepository() instanceof WorkspaceRepository )
289                 {
290                     modelRequest.setPomFile( pomArtifact.getFile() );
291                 }
292                 else
293                 {
294                     modelRequest.setModelSource( new FileModelSource( pomArtifact.getFile() ) );
295                 }
296 
297                 model = modelBuilder.build( modelRequest ).getEffectiveModel();
298             }
299             catch ( ModelBuildingException e )
300             {
301                 for ( ModelProblem problem : e.getProblems() )
302                 {
303                     if ( problem.getException() instanceof UnresolvableModelException )
304                     {
305                         result.addException( problem.getException() );
306                         throw new ArtifactDescriptorException( result );
307                     }
308                 }
309                 invalidDescriptor( session, trace, a, e );
310                 if ( ( getPolicy( session, a, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 )
311                 {
312                     return null;
313                 }
314                 result.addException( e );
315                 throw new ArtifactDescriptorException( result );
316             }
317 
318             Relocation relocation = getRelocation( model );
319 
320             if ( relocation != null )
321             {
322                 result.addRelocation( a );
323                 Artifact relocatedArtifact =
324                     new RelocatedArtifact( a, relocation.getGroupId(), relocation.getArtifactId(),
325                                            relocation.getVersion() );
326                 if ( LOGGER.isWarnEnabled() )
327                 {
328                     String message = "The artifact " + a + " has been relocated to " + relocatedArtifact;
329                     if ( relocation.getMessage() != null )
330                     {
331                         message += ": " + relocation.getMessage();
332                     }
333                     LOGGER.warn( message );
334                 }
335                 result.setArtifact( relocatedArtifact );
336                 a = relocatedArtifact;
337             }
338             else
339             {
340                 return model;
341             }
342         }
343     }
344 
345     private Properties toProperties( Map<String, String> dominant, Map<String, String> recessive )
346     {
347         Properties props = new Properties();
348         if ( recessive != null )
349         {
350             props.putAll( recessive );
351         }
352         if ( dominant != null )
353         {
354             props.putAll( dominant );
355         }
356         return props;
357     }
358 
359     private Relocation getRelocation( Model model )
360     {
361         Relocation relocation = null;
362         DistributionManagement distMgmt = model.getDistributionManagement();
363         if ( distMgmt != null )
364         {
365             relocation = distMgmt.getRelocation();
366         }
367         return relocation;
368     }
369 
370     private void missingDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
371                                     Exception exception )
372     {
373         RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_MISSING );
374         event.setTrace( trace );
375         event.setArtifact( artifact );
376         event.setException( exception );
377 
378         repositoryEventDispatcher.dispatch( event.build() );
379     }
380 
381     private void invalidDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
382                                     Exception exception )
383     {
384         RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_INVALID );
385         event.setTrace( trace );
386         event.setArtifact( artifact );
387         event.setException( exception );
388 
389         repositoryEventDispatcher.dispatch( event.build() );
390     }
391 
392     private int getPolicy( RepositorySystemSession session, Artifact a, ArtifactDescriptorRequest request )
393     {
394         ArtifactDescriptorPolicy policy = session.getArtifactDescriptorPolicy();
395         if ( policy == null )
396         {
397             return ArtifactDescriptorPolicy.STRICT;
398         }
399         return policy.getPolicy( session, new ArtifactDescriptorPolicyRequest( a, request.getRequestContext() ) );
400     }
401 
402 }