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