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