View Javadoc
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.internal;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.Collections;
26  import java.util.LinkedHashMap;
27  import java.util.LinkedHashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Objects;
31  import java.util.Properties;
32  import java.util.Set;
33  
34  import org.apache.maven.model.Model;
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.ModelBuildingResult;
42  import org.apache.maven.model.building.ModelProblem;
43  import org.apache.maven.model.building.ModelProblemUtils;
44  import org.apache.maven.model.resolution.UnresolvableModelException;
45  import org.apache.maven.repository.internal.relocation.DistributionManagementArtifactRelocationSource;
46  import org.apache.maven.repository.internal.relocation.UserPropertiesArtifactRelocationSource;
47  import org.codehaus.plexus.util.StringUtils;
48  import org.eclipse.aether.RepositoryEvent;
49  import org.eclipse.aether.RepositoryEvent.EventType;
50  import org.eclipse.aether.RepositoryException;
51  import org.eclipse.aether.RepositorySystemSession;
52  import org.eclipse.aether.RequestTrace;
53  import org.eclipse.aether.artifact.Artifact;
54  import org.eclipse.aether.impl.ArtifactDescriptorReader;
55  import org.eclipse.aether.impl.ArtifactResolver;
56  import org.eclipse.aether.impl.RemoteRepositoryManager;
57  import org.eclipse.aether.impl.RepositoryEventDispatcher;
58  import org.eclipse.aether.impl.VersionRangeResolver;
59  import org.eclipse.aether.impl.VersionResolver;
60  import org.eclipse.aether.repository.WorkspaceReader;
61  import org.eclipse.aether.repository.WorkspaceRepository;
62  import org.eclipse.aether.resolution.ArtifactDescriptorException;
63  import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
64  import org.eclipse.aether.resolution.ArtifactDescriptorPolicyRequest;
65  import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
66  import org.eclipse.aether.resolution.ArtifactDescriptorResult;
67  import org.eclipse.aether.resolution.ArtifactRequest;
68  import org.eclipse.aether.resolution.ArtifactResolutionException;
69  import org.eclipse.aether.resolution.ArtifactResult;
70  import org.eclipse.aether.resolution.VersionRequest;
71  import org.eclipse.aether.resolution.VersionResolutionException;
72  import org.eclipse.aether.resolution.VersionResult;
73  import org.eclipse.aether.spi.locator.Service;
74  import org.eclipse.aether.spi.locator.ServiceLocator;
75  import org.eclipse.aether.transfer.ArtifactNotFoundException;
76  import org.slf4j.Logger;
77  import org.slf4j.LoggerFactory;
78  
79  /**
80   * @author Benjamin Bentmann
81   */
82  @Named
83  @Singleton
84  public class DefaultArtifactDescriptorReader implements ArtifactDescriptorReader, Service {
85      private RemoteRepositoryManager remoteRepositoryManager;
86  
87      private VersionResolver versionResolver;
88  
89      private VersionRangeResolver versionRangeResolver;
90  
91      private ArtifactResolver artifactResolver;
92  
93      private RepositoryEventDispatcher repositoryEventDispatcher;
94  
95      private ModelBuilder modelBuilder;
96  
97      private ModelCacheFactory modelCacheFactory;
98  
99      private Map<String, MavenArtifactRelocationSource> artifactRelocationSources = Collections.emptyMap();
100 
101     private Map<String, ArtifactDescriptorDecorator> artifactDescriptorDecorators = Collections.emptyMap();
102 
103     private final ArtifactDescriptorReaderDelegate artifactDescriptorReaderDelegate =
104             new ArtifactDescriptorReaderDelegate();
105 
106     private final Logger logger = LoggerFactory.getLogger(getClass());
107 
108     /**
109      * Just here for "static" resolver providers.
110      */
111     @Deprecated
112     public DefaultArtifactDescriptorReader() {
113         // enable no-arg constructor
114     }
115 
116     /**
117      * Just here for "static" resolver providers.
118      */
119     @Deprecated
120     public DefaultArtifactDescriptorReader(
121             RemoteRepositoryManager remoteRepositoryManager,
122             VersionResolver versionResolver,
123             VersionRangeResolver versionRangeResolver,
124             ArtifactResolver artifactResolver,
125             ModelBuilder modelBuilder,
126             RepositoryEventDispatcher repositoryEventDispatcher,
127             ModelCacheFactory modelCacheFactory) {
128         setRemoteRepositoryManager(remoteRepositoryManager);
129         setVersionResolver(versionResolver);
130         setVersionRangeResolver(versionRangeResolver);
131         setArtifactResolver(artifactResolver);
132         setModelBuilder(modelBuilder);
133         setRepositoryEventDispatcher(repositoryEventDispatcher);
134         setModelCacheFactory(modelCacheFactory);
135         Map<String, MavenArtifactRelocationSource> artifactRelocationSources = new LinkedHashMap<>(); // order!
136         artifactRelocationSources.put(
137                 UserPropertiesArtifactRelocationSource.NAME, new UserPropertiesArtifactRelocationSource());
138         artifactRelocationSources.put(
139                 DistributionManagementArtifactRelocationSource.NAME,
140                 new DistributionManagementArtifactRelocationSource());
141         setArtifactRelocationSources(artifactRelocationSources);
142     }
143 
144     /**
145      * Just here for "static" resolver providers.
146      */
147     @SuppressWarnings("checkstyle:parameternumber")
148     @Deprecated
149     public DefaultArtifactDescriptorReader(
150             RemoteRepositoryManager remoteRepositoryManager,
151             VersionResolver versionResolver,
152             VersionRangeResolver versionRangeResolver,
153             ArtifactResolver artifactResolver,
154             ModelBuilder modelBuilder,
155             RepositoryEventDispatcher repositoryEventDispatcher,
156             ModelCacheFactory modelCacheFactory,
157             Map<String, MavenArtifactRelocationSource> artifactRelocationSources) {
158         setRemoteRepositoryManager(remoteRepositoryManager);
159         setVersionResolver(versionResolver);
160         setVersionRangeResolver(versionRangeResolver);
161         setArtifactResolver(artifactResolver);
162         setModelBuilder(modelBuilder);
163         setRepositoryEventDispatcher(repositoryEventDispatcher);
164         setModelCacheFactory(modelCacheFactory);
165         setArtifactRelocationSources(artifactRelocationSources);
166     }
167 
168     @SuppressWarnings("checkstyle:parameternumber")
169     @Inject
170     public DefaultArtifactDescriptorReader(
171             RemoteRepositoryManager remoteRepositoryManager,
172             VersionResolver versionResolver,
173             VersionRangeResolver versionRangeResolver,
174             ArtifactResolver artifactResolver,
175             ModelBuilder modelBuilder,
176             RepositoryEventDispatcher repositoryEventDispatcher,
177             ModelCacheFactory modelCacheFactory,
178             Map<String, MavenArtifactRelocationSource> artifactRelocationSources,
179             Map<String, ArtifactDescriptorDecorator> artifactDescriptorDecorators) {
180         setRemoteRepositoryManager(remoteRepositoryManager);
181         setVersionResolver(versionResolver);
182         setVersionRangeResolver(versionRangeResolver);
183         setArtifactResolver(artifactResolver);
184         setModelBuilder(modelBuilder);
185         setRepositoryEventDispatcher(repositoryEventDispatcher);
186         setModelCacheFactory(modelCacheFactory);
187         setArtifactRelocationSources(artifactRelocationSources);
188         setArtifactDescriptorDecorators(artifactDescriptorDecorators);
189     }
190 
191     @Deprecated
192     public void initService(ServiceLocator locator) {
193         setRemoteRepositoryManager(locator.getService(RemoteRepositoryManager.class));
194         setVersionResolver(locator.getService(VersionResolver.class));
195         setVersionRangeResolver(locator.getService(VersionRangeResolver.class));
196         setArtifactResolver(locator.getService(ArtifactResolver.class));
197         modelBuilder = locator.getService(ModelBuilder.class);
198         if (modelBuilder == null) {
199             setModelBuilder(new DefaultModelBuilderFactory().newInstance());
200         }
201         setRepositoryEventDispatcher(locator.getService(RepositoryEventDispatcher.class));
202         setModelCacheFactory(locator.getService(ModelCacheFactory.class));
203     }
204 
205     public DefaultArtifactDescriptorReader setRemoteRepositoryManager(RemoteRepositoryManager remoteRepositoryManager) {
206         this.remoteRepositoryManager =
207                 Objects.requireNonNull(remoteRepositoryManager, "remoteRepositoryManager cannot be null");
208         return this;
209     }
210 
211     public DefaultArtifactDescriptorReader setVersionResolver(VersionResolver versionResolver) {
212         this.versionResolver = Objects.requireNonNull(versionResolver, "versionResolver cannot be null");
213         return this;
214     }
215 
216     /** @since 3.2.2 */
217     public DefaultArtifactDescriptorReader setVersionRangeResolver(VersionRangeResolver versionRangeResolver) {
218         this.versionRangeResolver = Objects.requireNonNull(versionRangeResolver, "versionRangeResolver cannot be null");
219         return this;
220     }
221 
222     public DefaultArtifactDescriptorReader setArtifactResolver(ArtifactResolver artifactResolver) {
223         this.artifactResolver = Objects.requireNonNull(artifactResolver, "artifactResolver cannot be null");
224         return this;
225     }
226 
227     public DefaultArtifactDescriptorReader setRepositoryEventDispatcher(
228             RepositoryEventDispatcher repositoryEventDispatcher) {
229         this.repositoryEventDispatcher =
230                 Objects.requireNonNull(repositoryEventDispatcher, "repositoryEventDispatcher cannot be null");
231         return this;
232     }
233 
234     public DefaultArtifactDescriptorReader setModelBuilder(ModelBuilder modelBuilder) {
235         this.modelBuilder = Objects.requireNonNull(modelBuilder, "modelBuilder cannot be null");
236         return this;
237     }
238 
239     public DefaultArtifactDescriptorReader setModelCacheFactory(ModelCacheFactory modelCacheFactory) {
240         this.modelCacheFactory = Objects.requireNonNull(modelCacheFactory, "modelCacheFactory cannot be null");
241         return this;
242     }
243 
244     public DefaultArtifactDescriptorReader setArtifactRelocationSources(
245             Map<String, MavenArtifactRelocationSource> artifactRelocationSources) {
246         this.artifactRelocationSources =
247                 Objects.requireNonNull(artifactRelocationSources, "artifactRelocationSources cannot be null");
248         return this;
249     }
250 
251     public DefaultArtifactDescriptorReader setArtifactDescriptorDecorators(
252             Map<String, ArtifactDescriptorDecorator> artifactDescriptorDecorators) {
253         this.artifactDescriptorDecorators =
254                 Objects.requireNonNull(artifactDescriptorDecorators, "artifactDescriptorDecorators cannot be null");
255         return this;
256     }
257 
258     @Override
259     public ArtifactDescriptorResult readArtifactDescriptor(
260             RepositorySystemSession session, ArtifactDescriptorRequest request) throws ArtifactDescriptorException {
261         ArtifactDescriptorResult result = new ArtifactDescriptorResult(request);
262 
263         Model model = loadPom(session, request, result);
264         if (model != null) {
265             Map<String, Object> config = session.getConfigProperties();
266             ArtifactDescriptorReaderDelegate delegate =
267                     (ArtifactDescriptorReaderDelegate) config.get(ArtifactDescriptorReaderDelegate.class.getName());
268 
269             if (delegate == null) {
270                 delegate = artifactDescriptorReaderDelegate;
271             }
272 
273             delegate.populateResult(session, result, model);
274 
275             for (ArtifactDescriptorDecorator decorator : artifactDescriptorDecorators.values()) {
276                 decorator.populateArtifactDescriptor(session, result, model);
277             }
278         }
279 
280         return result;
281     }
282 
283     @SuppressWarnings("MethodLength")
284     private Model loadPom(
285             RepositorySystemSession session, ArtifactDescriptorRequest request, ArtifactDescriptorResult result)
286             throws ArtifactDescriptorException {
287         RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
288 
289         Set<String> visited = new LinkedHashSet<>();
290         for (Artifact a = request.getArtifact(); ; ) {
291             Artifact pomArtifact = ArtifactDescriptorUtils.toPomArtifact(a);
292             try {
293                 VersionRequest versionRequest =
294                         new VersionRequest(a, request.getRepositories(), request.getRequestContext());
295                 versionRequest.setTrace(trace);
296                 VersionResult versionResult = versionResolver.resolveVersion(session, versionRequest);
297 
298                 a = a.setVersion(versionResult.getVersion());
299 
300                 versionRequest =
301                         new VersionRequest(pomArtifact, request.getRepositories(), request.getRequestContext());
302                 versionRequest.setTrace(trace);
303                 versionResult = versionResolver.resolveVersion(session, versionRequest);
304 
305                 pomArtifact = pomArtifact.setVersion(versionResult.getVersion());
306             } catch (VersionResolutionException e) {
307                 result.addException(e);
308                 throw new ArtifactDescriptorException(result);
309             }
310 
311             if (!visited.add(a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getBaseVersion())) {
312                 RepositoryException exception =
313                         new RepositoryException("Artifact relocations form a cycle: " + visited);
314                 invalidDescriptor(session, trace, a, exception);
315                 if ((getPolicy(session, a, request) & ArtifactDescriptorPolicy.IGNORE_INVALID) != 0) {
316                     return null;
317                 }
318                 result.addException(exception);
319                 throw new ArtifactDescriptorException(result);
320             }
321 
322             ArtifactResult resolveResult;
323             try {
324                 ArtifactRequest resolveRequest =
325                         new ArtifactRequest(pomArtifact, request.getRepositories(), request.getRequestContext());
326                 resolveRequest.setTrace(trace);
327                 resolveResult = artifactResolver.resolveArtifact(session, resolveRequest);
328                 pomArtifact = resolveResult.getArtifact();
329                 result.setRepository(resolveResult.getRepository());
330             } catch (ArtifactResolutionException e) {
331                 if (e.getCause() instanceof ArtifactNotFoundException) {
332                     missingDescriptor(session, trace, a, (Exception) e.getCause());
333                     if ((getPolicy(session, a, request) & ArtifactDescriptorPolicy.IGNORE_MISSING) != 0) {
334                         return null;
335                     }
336                 }
337                 result.addException(e);
338                 throw new ArtifactDescriptorException(result);
339             }
340 
341             Model model;
342 
343             // TODO hack: don't rebuild model if it was already loaded during reactor resolution
344             final WorkspaceReader workspace = session.getWorkspaceReader();
345             if (workspace instanceof MavenWorkspaceReader) {
346                 model = ((MavenWorkspaceReader) workspace).findModel(pomArtifact);
347                 if (model != null) {
348                     return model;
349                 }
350             }
351 
352             try {
353                 ModelBuildingRequest modelRequest = new DefaultModelBuildingRequest();
354                 modelRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
355                 modelRequest.setProcessPlugins(false);
356                 modelRequest.setTwoPhaseBuilding(false);
357                 // This merge is on purpose because otherwise user properties would override model
358                 // properties in dependencies the user does not know. See MNG-7563 for details.
359                 modelRequest.setSystemProperties(
360                         toProperties(session.getUserProperties(), session.getSystemProperties()));
361                 modelRequest.setUserProperties(new Properties());
362                 modelRequest.setModelCache(modelCacheFactory.createCache(session));
363                 modelRequest.setModelResolver(new DefaultModelResolver(
364                         session,
365                         trace.newChild(modelRequest),
366                         request.getRequestContext(),
367                         artifactResolver,
368                         versionRangeResolver,
369                         remoteRepositoryManager,
370                         request.getRepositories()));
371                 if (resolveResult.getRepository() instanceof WorkspaceRepository) {
372                     modelRequest.setPomFile(pomArtifact.getFile());
373                 } else {
374                     modelRequest.setModelSource(new FileModelSource(pomArtifact.getFile()));
375                 }
376 
377                 ModelBuildingResult modelResult = modelBuilder.build(modelRequest);
378                 // ModelBuildingEx is thrown only on FATAL and ERROR severities, but we still can have WARNs
379                 // that may lead to unexpected build failure, log them
380                 if (!modelResult.getProblems().isEmpty()) {
381                     List<ModelProblem> problems = modelResult.getProblems();
382                     if (logger.isDebugEnabled()) {
383                         String problem = (problems.size() == 1) ? "problem" : "problems";
384                         String problemPredicate = problem + ((problems.size() == 1) ? " was" : " were");
385                         String message = String.format(
386                                 "%s %s encountered while building the effective model for %s during %s\n",
387                                 problems.size(),
388                                 problemPredicate,
389                                 request.getArtifact(),
390                                 RequestTraceHelper.interpretTrace(true, request.getTrace()));
391                         message += StringUtils.capitalizeFirstLetter(problem);
392                         for (ModelProblem modelProblem : problems) {
393                             message += String.format(
394                                     "\n* %s @ %s",
395                                     modelProblem.getMessage(), ModelProblemUtils.formatLocation(modelProblem, null));
396                         }
397                         logger.warn(message);
398                     } else {
399                         logger.warn(
400                                 "{} {} encountered while building the effective model for {} during {} (use -X to see details)",
401                                 problems.size(),
402                                 (problems.size() == 1) ? "problem was" : "problems were",
403                                 request.getArtifact(),
404                                 RequestTraceHelper.interpretTrace(false, request.getTrace()));
405                     }
406                 }
407                 model = modelResult.getEffectiveModel();
408             } catch (ModelBuildingException e) {
409                 for (ModelProblem problem : e.getProblems()) {
410                     if (problem.getException() instanceof UnresolvableModelException) {
411                         result.addException(problem.getException());
412                         throw new ArtifactDescriptorException(result);
413                     }
414                 }
415                 invalidDescriptor(session, trace, a, e);
416                 if ((getPolicy(session, a, request) & ArtifactDescriptorPolicy.IGNORE_INVALID) != 0) {
417                     return null;
418                 }
419                 result.addException(e);
420                 throw new ArtifactDescriptorException(result);
421             }
422 
423             Artifact relocatedArtifact = getRelocation(session, result, model);
424             if (relocatedArtifact != null) {
425                 if (withinSameGav(relocatedArtifact, a)) {
426                     result.setArtifact(relocatedArtifact);
427                     return model; // they share same model
428                 } else {
429                     result.addRelocation(a);
430                     a = relocatedArtifact;
431                     result.setArtifact(a);
432                 }
433             } else {
434                 return model;
435             }
436         }
437     }
438 
439     private boolean withinSameGav(Artifact a1, Artifact a2) {
440         return Objects.equals(a1.getGroupId(), a2.getGroupId())
441                 && Objects.equals(a1.getArtifactId(), a2.getArtifactId())
442                 && Objects.equals(a1.getVersion(), a2.getVersion());
443     }
444 
445     private Properties toProperties(Map<String, String> dominant, Map<String, String> recessive) {
446         Properties props = new Properties();
447         if (recessive != null) {
448             props.putAll(recessive);
449         }
450         if (dominant != null) {
451             props.putAll(dominant);
452         }
453         return props;
454     }
455 
456     private Artifact getRelocation(
457             RepositorySystemSession session, ArtifactDescriptorResult artifactDescriptorResult, Model model)
458             throws ArtifactDescriptorException {
459         Artifact result = null;
460         for (MavenArtifactRelocationSource source : artifactRelocationSources.values()) {
461             result = source.relocatedTarget(session, artifactDescriptorResult, model);
462             if (result != null) {
463                 break;
464             }
465         }
466         return result;
467     }
468 
469     private void missingDescriptor(
470             RepositorySystemSession session, RequestTrace trace, Artifact artifact, Exception exception) {
471         RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DESCRIPTOR_MISSING);
472         event.setTrace(trace);
473         event.setArtifact(artifact);
474         event.setException(exception);
475 
476         repositoryEventDispatcher.dispatch(event.build());
477     }
478 
479     private void invalidDescriptor(
480             RepositorySystemSession session, RequestTrace trace, Artifact artifact, Exception exception) {
481         RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DESCRIPTOR_INVALID);
482         event.setTrace(trace);
483         event.setArtifact(artifact);
484         event.setException(exception);
485 
486         repositoryEventDispatcher.dispatch(event.build());
487     }
488 
489     private int getPolicy(RepositorySystemSession session, Artifact a, ArtifactDescriptorRequest request) {
490         ArtifactDescriptorPolicy policy = session.getArtifactDescriptorPolicy();
491         if (policy == null) {
492             return ArtifactDescriptorPolicy.STRICT;
493         }
494         return policy.getPolicy(session, new ArtifactDescriptorPolicyRequest(a, request.getRequestContext()));
495     }
496 }