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