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.artifact.resolver;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.LinkedHashMap;
25  import java.util.LinkedHashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.concurrent.CountDownLatch;
30  import java.util.concurrent.Executor;
31  import java.util.concurrent.ExecutorService;
32  import java.util.concurrent.LinkedBlockingQueue;
33  import java.util.concurrent.ThreadFactory;
34  import java.util.concurrent.ThreadPoolExecutor;
35  import java.util.concurrent.TimeUnit;
36  import java.util.concurrent.atomic.AtomicInteger;
37  import java.util.regex.Matcher;
38  
39  import org.apache.maven.RepositoryUtils;
40  import org.apache.maven.artifact.Artifact;
41  import org.apache.maven.artifact.factory.ArtifactFactory;
42  import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
43  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
44  import org.apache.maven.artifact.metadata.ResolutionGroup;
45  import org.apache.maven.artifact.repository.ArtifactRepository;
46  import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager;
47  import org.apache.maven.artifact.repository.RepositoryRequest;
48  import org.apache.maven.artifact.repository.metadata.Snapshot;
49  import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata;
50  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
51  import org.apache.maven.execution.MavenSession;
52  import org.apache.maven.plugin.LegacySupport;
53  import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest;
54  import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
55  import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver;
56  import org.apache.maven.wagon.events.TransferListener;
57  import org.codehaus.plexus.PlexusContainer;
58  import org.codehaus.plexus.component.annotations.Component;
59  import org.codehaus.plexus.component.annotations.Requirement;
60  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
61  import org.codehaus.plexus.logging.Logger;
62  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable;
63  import org.eclipse.aether.RepositorySystem;
64  import org.eclipse.aether.RepositorySystemSession;
65  import org.eclipse.aether.repository.LocalRepositoryManager;
66  import org.eclipse.aether.resolution.ArtifactRequest;
67  import org.eclipse.aether.resolution.ArtifactResult;
68  
69  /**
70   * @author Jason van Zyl
71   */
72  @Component(role = ArtifactResolver.class)
73  public class DefaultArtifactResolver implements ArtifactResolver, Disposable {
74      @Requirement
75      private Logger logger;
76  
77      @Requirement
78      protected ArtifactFactory artifactFactory;
79  
80      @Requirement
81      private ArtifactCollector artifactCollector;
82  
83      @Requirement
84      private ResolutionErrorHandler resolutionErrorHandler;
85  
86      @Requirement
87      private ArtifactMetadataSource source;
88  
89      @Requirement
90      private PlexusContainer container;
91  
92      @Requirement
93      private LegacySupport legacySupport;
94  
95      @Requirement
96      private RepositorySystem repoSystem;
97  
98      private final Executor executor;
99  
100     public DefaultArtifactResolver() {
101         int threads = Integer.getInteger("maven.artifact.threads", 5);
102         if (threads <= 1) {
103             executor = Runnable::run;
104         } else {
105             executor = new ThreadPoolExecutor(
106                     threads, threads, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new DaemonThreadCreator());
107         }
108     }
109 
110     private RepositorySystemSession getSession(ArtifactRepository localRepository) {
111         return LegacyLocalRepositoryManager.overlay(localRepository, legacySupport.getRepositorySession(), repoSystem);
112     }
113 
114     private void injectSession1(RepositoryRequest request, MavenSession session) {
115         if (session != null) {
116             request.setOffline(session.isOffline());
117             request.setForceUpdate(session.getRequest().isUpdateSnapshots());
118         }
119     }
120 
121     private void injectSession2(ArtifactResolutionRequest request, MavenSession session) {
122         injectSession1(request, session);
123 
124         if (session != null) {
125             request.setServers(session.getRequest().getServers());
126             request.setMirrors(session.getRequest().getMirrors());
127             request.setProxies(session.getRequest().getProxies());
128         }
129     }
130 
131     public void resolve(
132             Artifact artifact,
133             List<ArtifactRepository> remoteRepositories,
134             ArtifactRepository localRepository,
135             TransferListener resolutionListener)
136             throws ArtifactResolutionException, ArtifactNotFoundException {
137         resolve(artifact, remoteRepositories, getSession(localRepository));
138     }
139 
140     public void resolveAlways(
141             Artifact artifact, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository)
142             throws ArtifactResolutionException, ArtifactNotFoundException {
143         resolve(artifact, remoteRepositories, getSession(localRepository));
144     }
145 
146     private void resolve(
147             Artifact artifact, List<ArtifactRepository> remoteRepositories, RepositorySystemSession session)
148             throws ArtifactResolutionException, ArtifactNotFoundException {
149         if (artifact == null) {
150             return;
151         }
152 
153         if (Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) {
154             File systemFile = artifact.getFile();
155 
156             if (systemFile == null) {
157                 throw new ArtifactNotFoundException("System artifact: " + artifact + " has no file attached", artifact);
158             }
159 
160             if (!systemFile.exists()) {
161                 throw new ArtifactNotFoundException(
162                         "System artifact: " + artifact + " not found in path: " + systemFile, artifact);
163             }
164 
165             if (!systemFile.isFile()) {
166                 throw new ArtifactNotFoundException(
167                         "System artifact: " + artifact + " is not a file: " + systemFile, artifact);
168             }
169 
170             artifact.setResolved(true);
171 
172             return;
173         }
174 
175         if (!artifact.isResolved()) {
176             ArtifactResult result;
177 
178             try {
179                 ArtifactRequest artifactRequest = new ArtifactRequest();
180                 artifactRequest.setArtifact(RepositoryUtils.toArtifact(artifact));
181                 artifactRequest.setRepositories(RepositoryUtils.toRepos(remoteRepositories));
182 
183                 // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
184                 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
185                 String path = lrm.getPathForLocalArtifact(artifactRequest.getArtifact());
186                 artifact.setFile(new File(lrm.getRepository().getBasedir(), path));
187 
188                 result = repoSystem.resolveArtifact(session, artifactRequest);
189             } catch (org.eclipse.aether.resolution.ArtifactResolutionException e) {
190                 if (e.getCause() instanceof org.eclipse.aether.transfer.ArtifactNotFoundException) {
191                     throw new ArtifactNotFoundException(e.getMessage(), artifact, remoteRepositories, e);
192                 } else {
193                     throw new ArtifactResolutionException(e.getMessage(), artifact, remoteRepositories, e);
194                 }
195             }
196 
197             artifact.selectVersion(result.getArtifact().getVersion());
198             artifact.setFile(result.getArtifact().getFile());
199             artifact.setResolved(true);
200 
201             if (artifact.isSnapshot()) {
202                 Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher(artifact.getVersion());
203                 if (matcher.matches()) {
204                     Snapshot snapshot = new Snapshot();
205                     snapshot.setTimestamp(matcher.group(2));
206                     try {
207                         snapshot.setBuildNumber(Integer.parseInt(matcher.group(3)));
208                         artifact.addMetadata(new SnapshotArtifactRepositoryMetadata(artifact, snapshot));
209                     } catch (NumberFormatException e) {
210                         logger.warn("Invalid artifact version " + artifact.getVersion() + ": " + e.getMessage());
211                     }
212                 }
213             }
214         }
215     }
216 
217     public ArtifactResolutionResult resolveTransitively(
218             Set<Artifact> artifacts,
219             Artifact originatingArtifact,
220             ArtifactRepository localRepository,
221             List<ArtifactRepository> remoteRepositories,
222             ArtifactMetadataSource source,
223             ArtifactFilter filter)
224             throws ArtifactResolutionException, ArtifactNotFoundException {
225         return resolveTransitively(
226                 artifacts,
227                 originatingArtifact,
228                 Collections.emptyMap(),
229                 localRepository,
230                 remoteRepositories,
231                 source,
232                 filter);
233     }
234 
235     public ArtifactResolutionResult resolveTransitively(
236             Set<Artifact> artifacts,
237             Artifact originatingArtifact,
238             Map<String, Artifact> managedVersions,
239             ArtifactRepository localRepository,
240             List<ArtifactRepository> remoteRepositories,
241             ArtifactMetadataSource source)
242             throws ArtifactResolutionException, ArtifactNotFoundException {
243         return resolveTransitively(
244                 artifacts, originatingArtifact, managedVersions, localRepository, remoteRepositories, source, null);
245     }
246 
247     public ArtifactResolutionResult resolveTransitively(
248             Set<Artifact> artifacts,
249             Artifact originatingArtifact,
250             Map<String, Artifact> managedVersions,
251             ArtifactRepository localRepository,
252             List<ArtifactRepository> remoteRepositories,
253             ArtifactMetadataSource source,
254             ArtifactFilter filter)
255             throws ArtifactResolutionException, ArtifactNotFoundException {
256         return resolveTransitively(
257                 artifacts,
258                 originatingArtifact,
259                 managedVersions,
260                 localRepository,
261                 remoteRepositories,
262                 source,
263                 filter,
264                 null);
265     }
266 
267     public ArtifactResolutionResult resolveTransitively(
268             Set<Artifact> artifacts,
269             Artifact originatingArtifact,
270             List<ArtifactRepository> remoteRepositories,
271             ArtifactRepository localRepository,
272             ArtifactMetadataSource source)
273             throws ArtifactResolutionException, ArtifactNotFoundException {
274         return resolveTransitively(artifacts, originatingArtifact, localRepository, remoteRepositories, source, null);
275     }
276 
277     public ArtifactResolutionResult resolveTransitively(
278             Set<Artifact> artifacts,
279             Artifact originatingArtifact,
280             List<ArtifactRepository> remoteRepositories,
281             ArtifactRepository localRepository,
282             ArtifactMetadataSource source,
283             List<ResolutionListener> listeners)
284             throws ArtifactResolutionException, ArtifactNotFoundException {
285         return resolveTransitively(
286                 artifacts,
287                 originatingArtifact,
288                 Collections.emptyMap(),
289                 localRepository,
290                 remoteRepositories,
291                 source,
292                 null,
293                 listeners);
294     }
295 
296     @SuppressWarnings("checkstyle:parameternumber")
297     public ArtifactResolutionResult resolveTransitively(
298             Set<Artifact> artifacts,
299             Artifact originatingArtifact,
300             Map<String, Artifact> managedVersions,
301             ArtifactRepository localRepository,
302             List<ArtifactRepository> remoteRepositories,
303             ArtifactMetadataSource source,
304             ArtifactFilter filter,
305             List<ResolutionListener> listeners)
306             throws ArtifactResolutionException, ArtifactNotFoundException {
307         return resolveTransitively(
308                 artifacts,
309                 originatingArtifact,
310                 managedVersions,
311                 localRepository,
312                 remoteRepositories,
313                 source,
314                 filter,
315                 listeners,
316                 null);
317     }
318 
319     @SuppressWarnings("checkstyle:parameternumber")
320     public ArtifactResolutionResult resolveTransitively(
321             Set<Artifact> artifacts,
322             Artifact originatingArtifact,
323             Map<String, Artifact> managedVersions,
324             ArtifactRepository localRepository,
325             List<ArtifactRepository> remoteRepositories,
326             ArtifactMetadataSource source,
327             ArtifactFilter filter,
328             List<ResolutionListener> listeners,
329             List<ConflictResolver> conflictResolvers)
330             throws ArtifactResolutionException, ArtifactNotFoundException {
331         ArtifactResolutionRequest request = new ArtifactResolutionRequest()
332                 .setArtifact(originatingArtifact)
333                 .setResolveRoot(false)
334                 .
335                 // This is required by the surefire plugin
336                 setArtifactDependencies(artifacts)
337                 .setManagedVersionMap(managedVersions)
338                 .setLocalRepository(localRepository)
339                 .setRemoteRepositories(remoteRepositories)
340                 .setCollectionFilter(filter)
341                 .setListeners(listeners);
342 
343         injectSession2(request, legacySupport.getSession());
344 
345         return resolveWithExceptions(request);
346     }
347 
348     public ArtifactResolutionResult resolveWithExceptions(ArtifactResolutionRequest request)
349             throws ArtifactResolutionException, ArtifactNotFoundException {
350         ArtifactResolutionResult result = resolve(request);
351 
352         // We have collected all the problems so let's mimic the way the old code worked and just blow up right here.
353         // That's right lets just let it rip right here and send a big incomprehensible blob of text at unsuspecting
354         // users. Bad dog!
355 
356         resolutionErrorHandler.throwErrors(request, result);
357 
358         return result;
359     }
360 
361     // ------------------------------------------------------------------------
362     //
363     // ------------------------------------------------------------------------
364 
365     @SuppressWarnings("checkstyle:methodlength")
366     public ArtifactResolutionResult resolve(ArtifactResolutionRequest request) {
367         Artifact rootArtifact = request.getArtifact();
368         Set<Artifact> artifacts = request.getArtifactDependencies();
369         Map<String, Artifact> managedVersions = request.getManagedVersionMap();
370         List<ResolutionListener> listeners = request.getListeners();
371         ArtifactFilter collectionFilter = request.getCollectionFilter();
372         ArtifactFilter resolutionFilter = request.getResolutionFilter();
373         RepositorySystemSession session = getSession(request.getLocalRepository());
374 
375         // TODO: hack because metadata isn't generated in m2e correctly and i want to run the maven i have in the
376         // workspace
377         if (source == null) {
378             try {
379                 source = container.lookup(ArtifactMetadataSource.class);
380             } catch (ComponentLookupException e) {
381                 // won't happen
382             }
383         }
384 
385         if (listeners == null) {
386             listeners = new ArrayList<>();
387 
388             if (logger.isDebugEnabled()) {
389                 listeners.add(new DebugResolutionListener(logger));
390             }
391 
392             listeners.add(new WarningResolutionListener(logger));
393         }
394 
395         ArtifactResolutionResult result = new ArtifactResolutionResult();
396 
397         // The root artifact may, or may not be resolved so we need to check before we attempt to resolve.
398         // This is often an artifact like a POM that is taken from disk and we already have hold of the
399         // file reference. But this may be a Maven Plugin that we need to resolve from a remote repository
400         // as well as its dependencies.
401 
402         if (request.isResolveRoot() /* && rootArtifact.getFile() == null */) {
403             try {
404                 resolve(rootArtifact, request.getRemoteRepositories(), session);
405             } catch (ArtifactResolutionException e) {
406                 result.addErrorArtifactException(e);
407                 return result;
408             } catch (ArtifactNotFoundException e) {
409                 result.addMissingArtifact(request.getArtifact());
410                 return result;
411             }
412         }
413 
414         ArtifactResolutionRequest collectionRequest = request;
415 
416         if (request.isResolveTransitively()) {
417             MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest(request);
418 
419             metadataRequest.setArtifact(rootArtifact);
420             metadataRequest.setResolveManagedVersions(managedVersions == null);
421 
422             try {
423                 ResolutionGroup resolutionGroup = source.retrieve(metadataRequest);
424 
425                 if (managedVersions == null) {
426                     managedVersions = resolutionGroup.getManagedVersions();
427                 }
428 
429                 Set<Artifact> directArtifacts = resolutionGroup.getArtifacts();
430 
431                 if (artifacts == null || artifacts.isEmpty()) {
432                     artifacts = directArtifacts;
433                 } else {
434                     List<Artifact> allArtifacts = new ArrayList<>();
435                     allArtifacts.addAll(artifacts);
436                     allArtifacts.addAll(directArtifacts);
437 
438                     Map<String, Artifact> mergedArtifacts = new LinkedHashMap<>();
439                     for (Artifact artifact : allArtifacts) {
440                         String conflictId = artifact.getDependencyConflictId();
441                         if (!mergedArtifacts.containsKey(conflictId)) {
442                             mergedArtifacts.put(conflictId, artifact);
443                         }
444                     }
445 
446                     artifacts = new LinkedHashSet<>(mergedArtifacts.values());
447                 }
448 
449                 collectionRequest = new ArtifactResolutionRequest(request);
450                 collectionRequest.setServers(request.getServers());
451                 collectionRequest.setMirrors(request.getMirrors());
452                 collectionRequest.setProxies(request.getProxies());
453                 collectionRequest.setRemoteRepositories(resolutionGroup.getResolutionRepositories());
454             } catch (ArtifactMetadataRetrievalException e) {
455                 ArtifactResolutionException are = new ArtifactResolutionException(
456                         "Unable to get dependency information for " + rootArtifact.getId() + ": " + e.getMessage(),
457                         rootArtifact,
458                         metadataRequest.getRemoteRepositories(),
459                         e);
460                 result.addMetadataResolutionException(are);
461                 return result;
462             }
463         }
464 
465         if (artifacts == null || artifacts.isEmpty()) {
466             if (request.isResolveRoot()) {
467                 result.addArtifact(rootArtifact);
468             }
469             return result;
470         }
471 
472         // After the collection we will have the artifact object in the result but they will not be resolved yet.
473         result = artifactCollector.collect(
474                 artifacts, rootArtifact, managedVersions, collectionRequest, source, collectionFilter, listeners, null);
475 
476         // We have metadata retrieval problems, or there are cycles that have been detected
477         // so we give this back to the calling code and let them deal with this information
478         // appropriately.
479 
480         if (result.hasMetadataResolutionExceptions()
481                 || result.hasVersionRangeViolations()
482                 || result.hasCircularDependencyExceptions()) {
483             return result;
484         }
485 
486         if (result.getArtifactResolutionNodes() != null) {
487             ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
488 
489             CountDownLatch latch =
490                     new CountDownLatch(result.getArtifactResolutionNodes().size());
491 
492             for (ResolutionNode node : result.getArtifactResolutionNodes()) {
493                 Artifact artifact = node.getArtifact();
494 
495                 if (resolutionFilter == null || resolutionFilter.include(artifact)) {
496                     executor.execute(new ResolveTask(
497                             classLoader, latch, artifact, session, node.getRemoteRepositories(), result));
498                 } else {
499                     latch.countDown();
500                 }
501             }
502             try {
503                 latch.await();
504             } catch (InterruptedException e) {
505                 result.addErrorArtifactException(
506                         new ArtifactResolutionException("Resolution interrupted", rootArtifact, e));
507             }
508         }
509 
510         // We want to send the root artifact back in the result but we need to do this after the other dependencies
511         // have been resolved.
512         if (request.isResolveRoot()) {
513             // Add the root artifact (as the first artifact to retain logical order of class path!)
514             Set<Artifact> allArtifacts = new LinkedHashSet<>();
515             allArtifacts.add(rootArtifact);
516             allArtifacts.addAll(result.getArtifacts());
517             result.setArtifacts(allArtifacts);
518         }
519 
520         return result;
521     }
522 
523     public void resolve(
524             Artifact artifact, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository)
525             throws ArtifactResolutionException, ArtifactNotFoundException {
526         resolve(artifact, remoteRepositories, localRepository, null);
527     }
528 
529     /**
530      * ThreadCreator for creating daemon threads with fixed ThreadGroup-name.
531      */
532     static final class DaemonThreadCreator implements ThreadFactory {
533         static final String THREADGROUP_NAME = "org.apache.maven.artifact.resolver.DefaultArtifactResolver";
534 
535         static final ThreadGroup GROUP = new ThreadGroup(THREADGROUP_NAME);
536 
537         static final AtomicInteger THREAD_NUMBER = new AtomicInteger(1);
538 
539         public Thread newThread(Runnable r) {
540             Thread newThread = new Thread(GROUP, r, "resolver-" + THREAD_NUMBER.getAndIncrement());
541             newThread.setDaemon(true);
542             newThread.setContextClassLoader(null);
543             return newThread;
544         }
545     }
546 
547     private class ResolveTask implements Runnable {
548 
549         private final ClassLoader classLoader;
550 
551         private final CountDownLatch latch;
552 
553         private final Artifact artifact;
554 
555         private final RepositorySystemSession session;
556 
557         private final List<ArtifactRepository> remoteRepositories;
558 
559         private final ArtifactResolutionResult result;
560 
561         ResolveTask(
562                 ClassLoader classLoader,
563                 CountDownLatch latch,
564                 Artifact artifact,
565                 RepositorySystemSession session,
566                 List<ArtifactRepository> remoteRepositories,
567                 ArtifactResolutionResult result) {
568             this.classLoader = classLoader;
569             this.latch = latch;
570             this.artifact = artifact;
571             this.session = session;
572             this.remoteRepositories = remoteRepositories;
573             this.result = result;
574         }
575 
576         public void run() {
577             ClassLoader old = Thread.currentThread().getContextClassLoader();
578             try {
579                 Thread.currentThread().setContextClassLoader(classLoader);
580                 resolve(artifact, remoteRepositories, session);
581             } catch (ArtifactNotFoundException anfe) {
582                 // These are cases where the artifact just isn't present in any of the remote repositories
583                 // because it wasn't deployed, or it was deployed in the wrong place.
584 
585                 synchronized (result) {
586                     result.addMissingArtifact(artifact);
587                 }
588             } catch (ArtifactResolutionException e) {
589                 // This is really a wagon TransferFailedException so something went wrong after we successfully
590                 // retrieved the metadata.
591 
592                 synchronized (result) {
593                     result.addErrorArtifactException(e);
594                 }
595             } finally {
596                 latch.countDown();
597                 Thread.currentThread().setContextClassLoader(old);
598             }
599         }
600     }
601 
602     @Override
603     public void dispose() {
604         if (executor instanceof ExecutorService) {
605             ((ExecutorService) executor).shutdownNow();
606         }
607     }
608 }