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