001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.impl;
020
021import javax.inject.Inject;
022import javax.inject.Named;
023import javax.inject.Singleton;
024
025import java.io.File;
026import java.io.IOException;
027import java.util.ArrayList;
028import java.util.Collection;
029import java.util.Collections;
030import java.util.Iterator;
031import java.util.List;
032import java.util.Map;
033import java.util.concurrent.atomic.AtomicBoolean;
034
035import org.eclipse.aether.RepositoryEvent;
036import org.eclipse.aether.RepositoryEvent.EventType;
037import org.eclipse.aether.RepositorySystemSession;
038import org.eclipse.aether.RequestTrace;
039import org.eclipse.aether.SyncContext;
040import org.eclipse.aether.artifact.Artifact;
041import org.eclipse.aether.artifact.ArtifactProperties;
042import org.eclipse.aether.impl.ArtifactResolver;
043import org.eclipse.aether.impl.OfflineController;
044import org.eclipse.aether.impl.RemoteRepositoryFilterManager;
045import org.eclipse.aether.impl.RemoteRepositoryManager;
046import org.eclipse.aether.impl.RepositoryConnectorProvider;
047import org.eclipse.aether.impl.RepositoryEventDispatcher;
048import org.eclipse.aether.impl.UpdateCheck;
049import org.eclipse.aether.impl.UpdateCheckManager;
050import org.eclipse.aether.impl.VersionResolver;
051import org.eclipse.aether.repository.ArtifactRepository;
052import org.eclipse.aether.repository.LocalArtifactRegistration;
053import org.eclipse.aether.repository.LocalArtifactRequest;
054import org.eclipse.aether.repository.LocalArtifactResult;
055import org.eclipse.aether.repository.LocalRepository;
056import org.eclipse.aether.repository.LocalRepositoryManager;
057import org.eclipse.aether.repository.RemoteRepository;
058import org.eclipse.aether.repository.RepositoryPolicy;
059import org.eclipse.aether.repository.WorkspaceReader;
060import org.eclipse.aether.resolution.ArtifactRequest;
061import org.eclipse.aether.resolution.ArtifactResolutionException;
062import org.eclipse.aether.resolution.ArtifactResult;
063import org.eclipse.aether.resolution.ResolutionErrorPolicy;
064import org.eclipse.aether.resolution.VersionRequest;
065import org.eclipse.aether.resolution.VersionResolutionException;
066import org.eclipse.aether.resolution.VersionResult;
067import org.eclipse.aether.spi.connector.ArtifactDownload;
068import org.eclipse.aether.spi.connector.RepositoryConnector;
069import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
070import org.eclipse.aether.spi.io.FileProcessor;
071import org.eclipse.aether.spi.locator.Service;
072import org.eclipse.aether.spi.locator.ServiceLocator;
073import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor;
074import org.eclipse.aether.spi.synccontext.SyncContextFactory;
075import org.eclipse.aether.transfer.ArtifactNotFoundException;
076import org.eclipse.aether.transfer.ArtifactTransferException;
077import org.eclipse.aether.transfer.NoRepositoryConnectorException;
078import org.eclipse.aether.transfer.RepositoryOfflineException;
079import org.eclipse.aether.util.ConfigUtils;
080import org.slf4j.Logger;
081import org.slf4j.LoggerFactory;
082
083import static java.util.Objects.requireNonNull;
084
085/**
086 *
087 */
088@Singleton
089@Named
090public class DefaultArtifactResolver implements ArtifactResolver, Service {
091
092    /**
093     * Configuration to enable "snapshot normalization", downloaded snapshots from remote with timestamped file names
094     * will have file names converted back to baseVersion. Default: {@code true}.
095     */
096    private static final String CONFIG_PROP_SNAPSHOT_NORMALIZATION = "aether.artifactResolver.snapshotNormalization";
097
098    /**
099     * Configuration to enable "interoperability" with Simple LRM, but this breaks RRF feature, hence this configuration
100     * is IGNORED when RRF is used, and is warmly recommended to leave it disabled even if no RRF is being used.
101     * Default: {@code false}.
102     */
103    private static final String CONFIG_PROP_SIMPLE_LRM_INTEROP = "aether.artifactResolver.simpleLrmInterop";
104
105    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultArtifactResolver.class);
106
107    private FileProcessor fileProcessor;
108
109    private RepositoryEventDispatcher repositoryEventDispatcher;
110
111    private VersionResolver versionResolver;
112
113    private UpdateCheckManager updateCheckManager;
114
115    private RepositoryConnectorProvider repositoryConnectorProvider;
116
117    private RemoteRepositoryManager remoteRepositoryManager;
118
119    private SyncContextFactory syncContextFactory;
120
121    private OfflineController offlineController;
122
123    private Map<String, ArtifactResolverPostProcessor> artifactResolverPostProcessors;
124
125    private RemoteRepositoryFilterManager remoteRepositoryFilterManager;
126
127    public DefaultArtifactResolver() {
128        // enables default constructor
129    }
130
131    @SuppressWarnings("checkstyle:parameternumber")
132    @Inject
133    DefaultArtifactResolver(
134            FileProcessor fileProcessor,
135            RepositoryEventDispatcher repositoryEventDispatcher,
136            VersionResolver versionResolver,
137            UpdateCheckManager updateCheckManager,
138            RepositoryConnectorProvider repositoryConnectorProvider,
139            RemoteRepositoryManager remoteRepositoryManager,
140            SyncContextFactory syncContextFactory,
141            OfflineController offlineController,
142            Map<String, ArtifactResolverPostProcessor> artifactResolverPostProcessors,
143            RemoteRepositoryFilterManager remoteRepositoryFilterManager) {
144        setFileProcessor(fileProcessor);
145        setRepositoryEventDispatcher(repositoryEventDispatcher);
146        setVersionResolver(versionResolver);
147        setUpdateCheckManager(updateCheckManager);
148        setRepositoryConnectorProvider(repositoryConnectorProvider);
149        setRemoteRepositoryManager(remoteRepositoryManager);
150        setSyncContextFactory(syncContextFactory);
151        setOfflineController(offlineController);
152        setArtifactResolverPostProcessors(artifactResolverPostProcessors);
153        setRemoteRepositoryFilterManager(remoteRepositoryFilterManager);
154    }
155
156    public void initService(ServiceLocator locator) {
157        setFileProcessor(locator.getService(FileProcessor.class));
158        setRepositoryEventDispatcher(locator.getService(RepositoryEventDispatcher.class));
159        setVersionResolver(locator.getService(VersionResolver.class));
160        setUpdateCheckManager(locator.getService(UpdateCheckManager.class));
161        setRepositoryConnectorProvider(locator.getService(RepositoryConnectorProvider.class));
162        setRemoteRepositoryManager(locator.getService(RemoteRepositoryManager.class));
163        setSyncContextFactory(locator.getService(SyncContextFactory.class));
164        setOfflineController(locator.getService(OfflineController.class));
165        setArtifactResolverPostProcessors(Collections.emptyMap());
166        setRemoteRepositoryFilterManager(locator.getService(RemoteRepositoryFilterManager.class));
167    }
168
169    /**
170     * @deprecated not used any more since MRESOLVER-36 move to slf4j, added back in MRESOLVER-64 for compatibility
171     */
172    @Deprecated
173    public DefaultArtifactResolver setLoggerFactory(org.eclipse.aether.spi.log.LoggerFactory loggerFactory) {
174        // this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() );
175        return this;
176    }
177
178    public DefaultArtifactResolver setFileProcessor(FileProcessor fileProcessor) {
179        this.fileProcessor = requireNonNull(fileProcessor, "file processor cannot be null");
180        return this;
181    }
182
183    public DefaultArtifactResolver setRepositoryEventDispatcher(RepositoryEventDispatcher repositoryEventDispatcher) {
184        this.repositoryEventDispatcher =
185                requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
186        return this;
187    }
188
189    public DefaultArtifactResolver setVersionResolver(VersionResolver versionResolver) {
190        this.versionResolver = requireNonNull(versionResolver, "version resolver cannot be null");
191        return this;
192    }
193
194    public DefaultArtifactResolver setUpdateCheckManager(UpdateCheckManager updateCheckManager) {
195        this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null");
196        return this;
197    }
198
199    public DefaultArtifactResolver setRepositoryConnectorProvider(
200            RepositoryConnectorProvider repositoryConnectorProvider) {
201        this.repositoryConnectorProvider =
202                requireNonNull(repositoryConnectorProvider, "repository connector provider cannot be null");
203        return this;
204    }
205
206    public DefaultArtifactResolver setRemoteRepositoryManager(RemoteRepositoryManager remoteRepositoryManager) {
207        this.remoteRepositoryManager =
208                requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null");
209        return this;
210    }
211
212    public DefaultArtifactResolver setSyncContextFactory(SyncContextFactory syncContextFactory) {
213        this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
214        return this;
215    }
216
217    public DefaultArtifactResolver setOfflineController(OfflineController offlineController) {
218        this.offlineController = requireNonNull(offlineController, "offline controller cannot be null");
219        return this;
220    }
221
222    public DefaultArtifactResolver setArtifactResolverPostProcessors(
223            Map<String, ArtifactResolverPostProcessor> artifactResolverPostProcessors) {
224        this.artifactResolverPostProcessors =
225                requireNonNull(artifactResolverPostProcessors, "artifact resolver post-processors cannot be null");
226        return this;
227    }
228
229    public DefaultArtifactResolver setRemoteRepositoryFilterManager(
230            RemoteRepositoryFilterManager remoteRepositoryFilterManager) {
231        this.remoteRepositoryFilterManager =
232                requireNonNull(remoteRepositoryFilterManager, "remote repository filter manager cannot be null");
233        return this;
234    }
235
236    public ArtifactResult resolveArtifact(RepositorySystemSession session, ArtifactRequest request)
237            throws ArtifactResolutionException {
238        requireNonNull(session, "session cannot be null");
239        requireNonNull(session, "session cannot be null");
240
241        return resolveArtifacts(session, Collections.singleton(request)).get(0);
242    }
243
244    public List<ArtifactResult> resolveArtifacts(
245            RepositorySystemSession session, Collection<? extends ArtifactRequest> requests)
246            throws ArtifactResolutionException {
247        requireNonNull(session, "session cannot be null");
248        requireNonNull(session, "session cannot be null");
249        try (SyncContext syncContext = syncContextFactory.newInstance(session, false)) {
250            Collection<Artifact> artifacts = new ArrayList<>(requests.size());
251            for (ArtifactRequest request : requests) {
252                if (request.getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null) != null) {
253                    continue;
254                }
255                artifacts.add(request.getArtifact());
256            }
257
258            syncContext.acquire(artifacts, null);
259
260            return resolve(session, requests);
261        }
262    }
263
264    @SuppressWarnings("checkstyle:methodlength")
265    private List<ArtifactResult> resolve(
266            RepositorySystemSession session, Collection<? extends ArtifactRequest> requests)
267            throws ArtifactResolutionException {
268        List<ArtifactResult> results = new ArrayList<>(requests.size());
269        boolean failures = false;
270        final boolean simpleLrmInterop = ConfigUtils.getBoolean(session, false, CONFIG_PROP_SIMPLE_LRM_INTEROP);
271
272        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
273        WorkspaceReader workspace = session.getWorkspaceReader();
274
275        List<ResolutionGroup> groups = new ArrayList<>();
276        // filter != null: means "filtering applied", if null no filtering applied (behave as before)
277        RemoteRepositoryFilter filter = remoteRepositoryFilterManager.getRemoteRepositoryFilter(session);
278
279        for (ArtifactRequest request : requests) {
280            RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
281
282            ArtifactResult result = new ArtifactResult(request);
283            results.add(result);
284
285            Artifact artifact = request.getArtifact();
286
287            artifactResolving(session, trace, artifact);
288
289            String localPath = artifact.getProperty(ArtifactProperties.LOCAL_PATH, null);
290            if (localPath != null) {
291                // unhosted artifact, just validate file
292                File file = new File(localPath);
293                if (!file.isFile()) {
294                    failures = true;
295                    result.addException(new ArtifactNotFoundException(artifact, null));
296                } else {
297                    artifact = artifact.setFile(file);
298                    result.setArtifact(artifact);
299                    artifactResolved(session, trace, artifact, null, result.getExceptions());
300                }
301                continue;
302            }
303
304            List<RemoteRepository> remoteRepositories = request.getRepositories();
305            List<RemoteRepository> filteredRemoteRepositories = new ArrayList<>(remoteRepositories);
306            if (filter != null) {
307                for (RemoteRepository repository : remoteRepositories) {
308                    RemoteRepositoryFilter.Result filterResult = filter.acceptArtifact(repository, artifact);
309                    if (!filterResult.isAccepted()) {
310                        result.addException(
311                                new ArtifactNotFoundException(artifact, repository, filterResult.reasoning()));
312                        filteredRemoteRepositories.remove(repository);
313                    }
314                }
315            }
316
317            VersionResult versionResult;
318            try {
319                VersionRequest versionRequest =
320                        new VersionRequest(artifact, filteredRemoteRepositories, request.getRequestContext());
321                versionRequest.setTrace(trace);
322                versionResult = versionResolver.resolveVersion(session, versionRequest);
323            } catch (VersionResolutionException e) {
324                result.addException(e);
325                continue;
326            }
327
328            artifact = artifact.setVersion(versionResult.getVersion());
329
330            if (versionResult.getRepository() != null) {
331                if (versionResult.getRepository() instanceof RemoteRepository) {
332                    filteredRemoteRepositories =
333                            Collections.singletonList((RemoteRepository) versionResult.getRepository());
334                } else {
335                    filteredRemoteRepositories = Collections.emptyList();
336                }
337            }
338
339            if (workspace != null) {
340                File file = workspace.findArtifact(artifact);
341                if (file != null) {
342                    artifact = artifact.setFile(file);
343                    result.setArtifact(artifact);
344                    result.setRepository(workspace.getRepository());
345                    artifactResolved(session, trace, artifact, result.getRepository(), null);
346                    continue;
347                }
348            }
349
350            LocalArtifactResult local = lrm.find(
351                    session,
352                    new LocalArtifactRequest(artifact, filteredRemoteRepositories, request.getRequestContext()));
353            boolean found = (filter != null && local.isAvailable()) || isLocallyInstalled(local, versionResult);
354            // with filtering it is availability that drives logic
355            // without filtering it is simply presence of file that drives the logic
356            // "interop" logic with simple LRM leads to RRF breakage: hence is ignored when filtering in effect
357            if (found) {
358                if (local.getRepository() != null) {
359                    result.setRepository(local.getRepository());
360                } else {
361                    result.setRepository(lrm.getRepository());
362                }
363
364                try {
365                    artifact = artifact.setFile(getFile(session, artifact, local.getFile()));
366                    result.setArtifact(artifact);
367                    artifactResolved(session, trace, artifact, result.getRepository(), null);
368                } catch (ArtifactTransferException e) {
369                    result.addException(e);
370                }
371                if (filter == null && simpleLrmInterop && !local.isAvailable()) {
372                    /*
373                     * NOTE: Interop with simple local repository: An artifact installed by a simple local repo
374                     * manager will not show up in the repository tracking file of the enhanced local repository.
375                     * If however the maven-metadata-local.xml tells us the artifact was installed locally, we
376                     * sync the repository tracking file.
377                     */
378                    lrm.add(session, new LocalArtifactRegistration(artifact));
379                }
380
381                continue;
382            }
383
384            if (local.getFile() != null) {
385                LOGGER.debug("Verifying availability of {} from {}", local.getFile(), remoteRepositories);
386            }
387
388            LOGGER.debug("Resolving artifact {} from {}", artifact, remoteRepositories);
389            AtomicBoolean resolved = new AtomicBoolean(false);
390            Iterator<ResolutionGroup> groupIt = groups.iterator();
391            for (RemoteRepository repo : filteredRemoteRepositories) {
392                if (!repo.getPolicy(artifact.isSnapshot()).isEnabled()) {
393                    continue;
394                }
395
396                try {
397                    Utils.checkOffline(session, offlineController, repo);
398                } catch (RepositoryOfflineException e) {
399                    Exception exception = new ArtifactNotFoundException(
400                            artifact,
401                            repo,
402                            "Cannot access " + repo.getId() + " ("
403                                    + repo.getUrl() + ") in offline mode and the artifact " + artifact
404                                    + " has not been downloaded from it before.",
405                            e);
406                    result.addException(exception);
407                    continue;
408                }
409
410                ResolutionGroup group = null;
411                while (groupIt.hasNext()) {
412                    ResolutionGroup t = groupIt.next();
413                    if (t.matches(repo)) {
414                        group = t;
415                        break;
416                    }
417                }
418                if (group == null) {
419                    group = new ResolutionGroup(repo);
420                    groups.add(group);
421                    groupIt = Collections.emptyIterator();
422                }
423                group.items.add(new ResolutionItem(trace, artifact, resolved, result, local, repo));
424            }
425        }
426
427        for (ResolutionGroup group : groups) {
428            performDownloads(session, group);
429        }
430
431        for (ArtifactResolverPostProcessor artifactResolverPostProcessor : artifactResolverPostProcessors.values()) {
432            artifactResolverPostProcessor.postProcess(session, results);
433        }
434
435        for (ArtifactResult result : results) {
436            ArtifactRequest request = result.getRequest();
437
438            Artifact artifact = result.getArtifact();
439            if (artifact == null || artifact.getFile() == null) {
440                failures = true;
441                if (result.getExceptions().isEmpty()) {
442                    Exception exception = new ArtifactNotFoundException(request.getArtifact(), null);
443                    result.addException(exception);
444                }
445                RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
446                artifactResolved(session, trace, request.getArtifact(), null, result.getExceptions());
447            }
448        }
449
450        if (failures) {
451            throw new ArtifactResolutionException(results);
452        }
453
454        return results;
455    }
456
457    private boolean isLocallyInstalled(LocalArtifactResult lar, VersionResult vr) {
458        if (lar.isAvailable()) {
459            return true;
460        }
461        if (lar.getFile() != null) {
462            if (vr.getRepository() instanceof LocalRepository) {
463                // resolution of (snapshot) version found locally installed artifact
464                return true;
465            } else if (vr.getRepository() == null
466                    && lar.getRequest().getRepositories().isEmpty()) {
467                // resolution of version range found locally installed artifact
468                return true;
469            }
470        }
471        return false;
472    }
473
474    private File getFile(RepositorySystemSession session, Artifact artifact, File file)
475            throws ArtifactTransferException {
476        if (artifact.isSnapshot()
477                && !artifact.getVersion().equals(artifact.getBaseVersion())
478                && ConfigUtils.getBoolean(session, true, CONFIG_PROP_SNAPSHOT_NORMALIZATION)) {
479            String name = file.getName().replace(artifact.getVersion(), artifact.getBaseVersion());
480            File dst = new File(file.getParent(), name);
481
482            boolean copy = dst.length() != file.length() || dst.lastModified() != file.lastModified();
483            if (copy) {
484                try {
485                    fileProcessor.copy(file, dst);
486                    dst.setLastModified(file.lastModified());
487                } catch (IOException e) {
488                    throw new ArtifactTransferException(artifact, null, e);
489                }
490            }
491
492            file = dst;
493        }
494
495        return file;
496    }
497
498    private void performDownloads(RepositorySystemSession session, ResolutionGroup group) {
499        List<ArtifactDownload> downloads = gatherDownloads(session, group);
500        if (downloads.isEmpty()) {
501            return;
502        }
503
504        for (ArtifactDownload download : downloads) {
505            artifactDownloading(session, download.getTrace(), download.getArtifact(), group.repository);
506        }
507
508        try {
509            try (RepositoryConnector connector =
510                    repositoryConnectorProvider.newRepositoryConnector(session, group.repository)) {
511                connector.get(downloads, null);
512            }
513        } catch (NoRepositoryConnectorException e) {
514            for (ArtifactDownload download : downloads) {
515                download.setException(new ArtifactTransferException(download.getArtifact(), group.repository, e));
516            }
517        }
518
519        evaluateDownloads(session, group);
520    }
521
522    private List<ArtifactDownload> gatherDownloads(RepositorySystemSession session, ResolutionGroup group) {
523        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
524        List<ArtifactDownload> downloads = new ArrayList<>();
525
526        for (ResolutionItem item : group.items) {
527            Artifact artifact = item.artifact;
528
529            if (item.resolved.get()) {
530                // resolved in previous resolution group
531                continue;
532            }
533
534            ArtifactDownload download = new ArtifactDownload();
535            download.setArtifact(artifact);
536            download.setRequestContext(item.request.getRequestContext());
537            download.setListener(SafeTransferListener.wrap(session));
538            download.setTrace(item.trace);
539            if (item.local.getFile() != null) {
540                download.setFile(item.local.getFile());
541                download.setExistenceCheck(true);
542            } else {
543                String path =
544                        lrm.getPathForRemoteArtifact(artifact, group.repository, item.request.getRequestContext());
545                download.setFile(new File(lrm.getRepository().getBasedir(), path));
546            }
547
548            boolean snapshot = artifact.isSnapshot();
549            RepositoryPolicy policy = remoteRepositoryManager.getPolicy(session, group.repository, !snapshot, snapshot);
550
551            int errorPolicy = Utils.getPolicy(session, artifact, group.repository);
552            if ((errorPolicy & ResolutionErrorPolicy.CACHE_ALL) != 0) {
553                UpdateCheck<Artifact, ArtifactTransferException> check = new UpdateCheck<>();
554                check.setItem(artifact);
555                check.setFile(download.getFile());
556                check.setFileValid(false);
557                check.setRepository(group.repository);
558                check.setPolicy(policy.getUpdatePolicy());
559                item.updateCheck = check;
560                updateCheckManager.checkArtifact(session, check);
561                if (!check.isRequired()) {
562                    item.result.addException(check.getException());
563                    continue;
564                }
565            }
566
567            download.setChecksumPolicy(policy.getChecksumPolicy());
568            download.setRepositories(item.repository.getMirroredRepositories());
569            downloads.add(download);
570            item.download = download;
571        }
572
573        return downloads;
574    }
575
576    private void evaluateDownloads(RepositorySystemSession session, ResolutionGroup group) {
577        LocalRepositoryManager lrm = session.getLocalRepositoryManager();
578
579        for (ResolutionItem item : group.items) {
580            ArtifactDownload download = item.download;
581            if (download == null) {
582                continue;
583            }
584
585            Artifact artifact = download.getArtifact();
586            if (download.getException() == null) {
587                item.resolved.set(true);
588                item.result.setRepository(group.repository);
589                try {
590                    artifact = artifact.setFile(getFile(session, artifact, download.getFile()));
591                    item.result.setArtifact(artifact);
592
593                    lrm.add(
594                            session,
595                            new LocalArtifactRegistration(artifact, group.repository, download.getSupportedContexts()));
596                } catch (ArtifactTransferException e) {
597                    download.setException(e);
598                    item.result.addException(e);
599                }
600            } else {
601                item.result.addException(download.getException());
602            }
603
604            /*
605             * NOTE: Touch after registration with local repo to ensure concurrent resolution is not rejected with
606             * "already updated" via session data when actual update to local repo is still pending.
607             */
608            if (item.updateCheck != null) {
609                item.updateCheck.setException(download.getException());
610                updateCheckManager.touchArtifact(session, item.updateCheck);
611            }
612
613            artifactDownloaded(session, download.getTrace(), artifact, group.repository, download.getException());
614            if (download.getException() == null) {
615                artifactResolved(session, download.getTrace(), artifact, group.repository, null);
616            }
617        }
618    }
619
620    private void artifactResolving(RepositorySystemSession session, RequestTrace trace, Artifact artifact) {
621        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_RESOLVING);
622        event.setTrace(trace);
623        event.setArtifact(artifact);
624
625        repositoryEventDispatcher.dispatch(event.build());
626    }
627
628    private void artifactResolved(
629            RepositorySystemSession session,
630            RequestTrace trace,
631            Artifact artifact,
632            ArtifactRepository repository,
633            List<Exception> exceptions) {
634        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_RESOLVED);
635        event.setTrace(trace);
636        event.setArtifact(artifact);
637        event.setRepository(repository);
638        event.setExceptions(exceptions);
639        if (artifact != null) {
640            event.setFile(artifact.getFile());
641        }
642
643        repositoryEventDispatcher.dispatch(event.build());
644    }
645
646    private void artifactDownloading(
647            RepositorySystemSession session, RequestTrace trace, Artifact artifact, RemoteRepository repository) {
648        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DOWNLOADING);
649        event.setTrace(trace);
650        event.setArtifact(artifact);
651        event.setRepository(repository);
652
653        repositoryEventDispatcher.dispatch(event.build());
654    }
655
656    private void artifactDownloaded(
657            RepositorySystemSession session,
658            RequestTrace trace,
659            Artifact artifact,
660            RemoteRepository repository,
661            Exception exception) {
662        RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.ARTIFACT_DOWNLOADED);
663        event.setTrace(trace);
664        event.setArtifact(artifact);
665        event.setRepository(repository);
666        event.setException(exception);
667        if (artifact != null) {
668            event.setFile(artifact.getFile());
669        }
670
671        repositoryEventDispatcher.dispatch(event.build());
672    }
673
674    static class ResolutionGroup {
675
676        final RemoteRepository repository;
677
678        final List<ResolutionItem> items = new ArrayList<>();
679
680        ResolutionGroup(RemoteRepository repository) {
681            this.repository = repository;
682        }
683
684        boolean matches(RemoteRepository repo) {
685            return repository.getUrl().equals(repo.getUrl())
686                    && repository.getContentType().equals(repo.getContentType())
687                    && repository.isRepositoryManager() == repo.isRepositoryManager();
688        }
689    }
690
691    static class ResolutionItem {
692
693        final RequestTrace trace;
694
695        final ArtifactRequest request;
696
697        final ArtifactResult result;
698
699        final LocalArtifactResult local;
700
701        final RemoteRepository repository;
702
703        final Artifact artifact;
704
705        final AtomicBoolean resolved;
706
707        ArtifactDownload download;
708
709        UpdateCheck<Artifact, ArtifactTransferException> updateCheck;
710
711        ResolutionItem(
712                RequestTrace trace,
713                Artifact artifact,
714                AtomicBoolean resolved,
715                ArtifactResult result,
716                LocalArtifactResult local,
717                RemoteRepository repository) {
718            this.trace = trace;
719            this.artifact = artifact;
720            this.resolved = resolved;
721            this.result = result;
722            this.request = result.getRequest();
723            this.local = local;
724            this.repository = repository;
725        }
726    }
727}