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