1 package org.eclipse.aether.internal.impl;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.Iterator;
28 import java.util.List;
29 import static java.util.Objects.requireNonNull;
30 import java.util.concurrent.atomic.AtomicBoolean;
31
32 import javax.inject.Inject;
33 import javax.inject.Named;
34 import javax.inject.Singleton;
35
36 import org.eclipse.aether.RepositoryEvent;
37 import org.eclipse.aether.RepositoryEvent.EventType;
38 import org.eclipse.aether.RepositorySystemSession;
39 import org.eclipse.aether.RequestTrace;
40 import org.eclipse.aether.SyncContext;
41 import org.eclipse.aether.artifact.Artifact;
42 import org.eclipse.aether.artifact.ArtifactProperties;
43 import org.eclipse.aether.impl.ArtifactResolver;
44 import org.eclipse.aether.impl.OfflineController;
45 import org.eclipse.aether.impl.RemoteRepositoryManager;
46 import org.eclipse.aether.impl.RepositoryConnectorProvider;
47 import org.eclipse.aether.impl.RepositoryEventDispatcher;
48 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
49 import org.eclipse.aether.impl.UpdateCheck;
50 import org.eclipse.aether.impl.UpdateCheckManager;
51 import org.eclipse.aether.impl.VersionResolver;
52 import org.eclipse.aether.repository.ArtifactRepository;
53 import org.eclipse.aether.repository.LocalArtifactRegistration;
54 import org.eclipse.aether.repository.LocalArtifactRequest;
55 import org.eclipse.aether.repository.LocalArtifactResult;
56 import org.eclipse.aether.repository.LocalRepository;
57 import org.eclipse.aether.repository.LocalRepositoryManager;
58 import org.eclipse.aether.repository.RemoteRepository;
59 import org.eclipse.aether.repository.RepositoryPolicy;
60 import org.eclipse.aether.repository.WorkspaceReader;
61 import org.eclipse.aether.resolution.ArtifactRequest;
62 import org.eclipse.aether.resolution.ArtifactResolutionException;
63 import org.eclipse.aether.resolution.ArtifactResult;
64 import org.eclipse.aether.resolution.ResolutionErrorPolicy;
65 import org.eclipse.aether.resolution.VersionRequest;
66 import org.eclipse.aether.resolution.VersionResolutionException;
67 import org.eclipse.aether.resolution.VersionResult;
68 import org.eclipse.aether.spi.connector.ArtifactDownload;
69 import org.eclipse.aether.spi.connector.RepositoryConnector;
70 import org.eclipse.aether.spi.io.FileProcessor;
71 import org.eclipse.aether.spi.locator.Service;
72 import org.eclipse.aether.spi.locator.ServiceLocator;
73 import org.eclipse.aether.transfer.ArtifactNotFoundException;
74 import org.eclipse.aether.transfer.ArtifactTransferException;
75 import org.eclipse.aether.transfer.NoRepositoryConnectorException;
76 import org.eclipse.aether.transfer.RepositoryOfflineException;
77 import org.eclipse.aether.util.ConfigUtils;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
80
81
82
83 @Singleton
84 @Named
85 public class DefaultArtifactResolver
86 implements ArtifactResolver, Service
87 {
88
89 private static final String CONFIG_PROP_SNAPSHOT_NORMALIZATION = "aether.artifactResolver.snapshotNormalization";
90
91 private static final Logger LOGGER = LoggerFactory.getLogger( DefaultArtifactResolver.class );
92
93 private FileProcessor fileProcessor;
94
95 private RepositoryEventDispatcher repositoryEventDispatcher;
96
97 private VersionResolver versionResolver;
98
99 private UpdateCheckManager updateCheckManager;
100
101 private RepositoryConnectorProvider repositoryConnectorProvider;
102
103 private RemoteRepositoryManager remoteRepositoryManager;
104
105 private SyncContextFactory syncContextFactory;
106
107 private OfflineController offlineController;
108
109 public DefaultArtifactResolver()
110 {
111
112 }
113
114 @SuppressWarnings( "checkstyle:parameternumber" )
115 @Inject
116 DefaultArtifactResolver( FileProcessor fileProcessor, RepositoryEventDispatcher repositoryEventDispatcher,
117 VersionResolver versionResolver, UpdateCheckManager updateCheckManager,
118 RepositoryConnectorProvider repositoryConnectorProvider,
119 RemoteRepositoryManager remoteRepositoryManager, SyncContextFactory syncContextFactory,
120 OfflineController offlineController )
121 {
122 setFileProcessor( fileProcessor );
123 setRepositoryEventDispatcher( repositoryEventDispatcher );
124 setVersionResolver( versionResolver );
125 setUpdateCheckManager( updateCheckManager );
126 setRepositoryConnectorProvider( repositoryConnectorProvider );
127 setRemoteRepositoryManager( remoteRepositoryManager );
128 setSyncContextFactory( syncContextFactory );
129 setOfflineController( offlineController );
130 }
131
132 public void initService( ServiceLocator locator )
133 {
134 setFileProcessor( locator.getService( FileProcessor.class ) );
135 setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
136 setVersionResolver( locator.getService( VersionResolver.class ) );
137 setUpdateCheckManager( locator.getService( UpdateCheckManager.class ) );
138 setRepositoryConnectorProvider( locator.getService( RepositoryConnectorProvider.class ) );
139 setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
140 setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
141 setOfflineController( locator.getService( OfflineController.class ) );
142 }
143
144
145
146
147 @Deprecated
148 public DefaultArtifactResolver setLoggerFactory( org.eclipse.aether.spi.log.LoggerFactory loggerFactory )
149 {
150
151 return this;
152 }
153
154 public DefaultArtifactResolver setFileProcessor( FileProcessor fileProcessor )
155 {
156 this.fileProcessor = requireNonNull( fileProcessor, "file processor cannot be null" );
157 return this;
158 }
159
160 public DefaultArtifactResolver setRepositoryEventDispatcher( RepositoryEventDispatcher repositoryEventDispatcher )
161 {
162 this.repositoryEventDispatcher = requireNonNull( repositoryEventDispatcher,
163 "repository event dispatcher cannot be null" );
164 return this;
165 }
166
167 public DefaultArtifactResolver setVersionResolver( VersionResolver versionResolver )
168 {
169 this.versionResolver = requireNonNull( versionResolver, "version resolver cannot be null" );
170 return this;
171 }
172
173 public DefaultArtifactResolver setUpdateCheckManager( UpdateCheckManager updateCheckManager )
174 {
175 this.updateCheckManager = requireNonNull( updateCheckManager, "update check manager cannot be null" );
176 return this;
177 }
178
179 public DefaultArtifactResolver setRepositoryConnectorProvider(
180 RepositoryConnectorProvider repositoryConnectorProvider )
181 {
182 this.repositoryConnectorProvider = requireNonNull( repositoryConnectorProvider,
183 "repository connector provider cannot be null" );
184 return this;
185 }
186
187 public DefaultArtifactResolver setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
188 {
189 this.remoteRepositoryManager = requireNonNull( remoteRepositoryManager,
190 "remote repository provider cannot be null" );
191 return this;
192 }
193
194 public DefaultArtifactResolver setSyncContextFactory( SyncContextFactory syncContextFactory )
195 {
196 this.syncContextFactory = requireNonNull( syncContextFactory, "sync context factory cannot be null" );
197 return this;
198 }
199
200 public DefaultArtifactResolver setOfflineController( OfflineController offlineController )
201 {
202 this.offlineController = requireNonNull( offlineController, "offline controller cannot be null" );
203 return this;
204 }
205
206 public ArtifactResult resolveArtifact( RepositorySystemSession session, ArtifactRequest request )
207 throws ArtifactResolutionException
208 {
209 return resolveArtifacts( session, Collections.singleton( request ) ).get( 0 );
210 }
211
212 public List<ArtifactResult> resolveArtifacts( RepositorySystemSession session,
213 Collection<? extends ArtifactRequest> requests )
214 throws ArtifactResolutionException
215 {
216
217 try ( SyncContext syncContext = syncContextFactory.newInstance( session, false ) )
218 {
219 Collection<Artifact> artifacts = new ArrayList<>( requests.size() );
220 for ( ArtifactRequest request : requests )
221 {
222 if ( request.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) != null )
223 {
224 continue;
225 }
226 artifacts.add( request.getArtifact() );
227 }
228
229 syncContext.acquire( artifacts, null );
230
231 return resolve( session, requests );
232 }
233 }
234
235 @SuppressWarnings( "checkstyle:methodlength" )
236 private List<ArtifactResult> resolve( RepositorySystemSession session,
237 Collection<? extends ArtifactRequest> requests )
238 throws ArtifactResolutionException
239 {
240 List<ArtifactResult> results = new ArrayList<>( requests.size() );
241 boolean failures = false;
242
243 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
244 WorkspaceReader workspace = session.getWorkspaceReader();
245
246 List<ResolutionGroup> groups = new ArrayList<>();
247
248 for ( ArtifactRequest request : requests )
249 {
250 RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
251
252 ArtifactResult result = new ArtifactResult( request );
253 results.add( result );
254
255 Artifact artifact = request.getArtifact();
256 List<RemoteRepository> repos = request.getRepositories();
257
258 artifactResolving( session, trace, artifact );
259
260 String localPath = artifact.getProperty( ArtifactProperties.LOCAL_PATH, null );
261 if ( localPath != null )
262 {
263
264 File file = new File( localPath );
265 if ( !file.isFile() )
266 {
267 failures = true;
268 result.addException( new ArtifactNotFoundException( artifact, null ) );
269 }
270 else
271 {
272 artifact = artifact.setFile( file );
273 result.setArtifact( artifact );
274 artifactResolved( session, trace, artifact, null, result.getExceptions() );
275 }
276 continue;
277 }
278
279 VersionResult versionResult;
280 try
281 {
282 VersionRequest versionRequest = new VersionRequest( artifact, repos, request.getRequestContext() );
283 versionRequest.setTrace( trace );
284 versionResult = versionResolver.resolveVersion( session, versionRequest );
285 }
286 catch ( VersionResolutionException e )
287 {
288 result.addException( e );
289 continue;
290 }
291
292 artifact = artifact.setVersion( versionResult.getVersion() );
293
294 if ( versionResult.getRepository() != null )
295 {
296 if ( versionResult.getRepository() instanceof RemoteRepository )
297 {
298 repos = Collections.singletonList( (RemoteRepository) versionResult.getRepository() );
299 }
300 else
301 {
302 repos = Collections.emptyList();
303 }
304 }
305
306 if ( workspace != null )
307 {
308 File file = workspace.findArtifact( artifact );
309 if ( file != null )
310 {
311 artifact = artifact.setFile( file );
312 result.setArtifact( artifact );
313 result.setRepository( workspace.getRepository() );
314 artifactResolved( session, trace, artifact, result.getRepository(), null );
315 continue;
316 }
317 }
318
319 LocalArtifactResult local =
320 lrm.find( session, new LocalArtifactRequest( artifact, repos, request.getRequestContext() ) );
321 if ( isLocallyInstalled( local, versionResult ) )
322 {
323 if ( local.getRepository() != null )
324 {
325 result.setRepository( local.getRepository() );
326 }
327 else
328 {
329 result.setRepository( lrm.getRepository() );
330 }
331 try
332 {
333 artifact = artifact.setFile( getFile( session, artifact, local.getFile() ) );
334 result.setArtifact( artifact );
335 artifactResolved( session, trace, artifact, result.getRepository(), null );
336 }
337 catch ( ArtifactTransferException e )
338 {
339 result.addException( e );
340 }
341 if ( !local.isAvailable() )
342 {
343
344
345
346
347
348
349 lrm.add( session, new LocalArtifactRegistration( artifact ) );
350 }
351 continue;
352 }
353 else if ( local.getFile() != null )
354 {
355 LOGGER.debug( "Verifying availability of {} from {}", local.getFile(), repos );
356 }
357
358 LOGGER.debug( "Resolving artifact {} from {}", artifact, repos );
359 AtomicBoolean resolved = new AtomicBoolean( false );
360 Iterator<ResolutionGroup> groupIt = groups.iterator();
361 for ( RemoteRepository repo : repos )
362 {
363 if ( !repo.getPolicy( artifact.isSnapshot() ).isEnabled() )
364 {
365 continue;
366 }
367
368 try
369 {
370 Utils.checkOffline( session, offlineController, repo );
371 }
372 catch ( RepositoryOfflineException e )
373 {
374 Exception exception =
375 new ArtifactNotFoundException( artifact, repo, "Cannot access " + repo.getId() + " ("
376 + repo.getUrl() + ") in offline mode and the artifact " + artifact
377 + " has not been downloaded from it before.", e );
378 result.addException( exception );
379 continue;
380 }
381
382 ResolutionGroup group = null;
383 while ( groupIt.hasNext() )
384 {
385 ResolutionGroup t = groupIt.next();
386 if ( t.matches( repo ) )
387 {
388 group = t;
389 break;
390 }
391 }
392 if ( group == null )
393 {
394 group = new ResolutionGroup( repo );
395 groups.add( group );
396 groupIt = Collections.<ResolutionGroup>emptyList().iterator();
397 }
398 group.items.add( new ResolutionItem( trace, artifact, resolved, result, local, repo ) );
399 }
400 }
401
402 for ( ResolutionGroup group : groups )
403 {
404 performDownloads( session, group );
405 }
406
407 for ( ArtifactResult result : results )
408 {
409 ArtifactRequest request = result.getRequest();
410
411 Artifact artifact = result.getArtifact();
412 if ( artifact == null || artifact.getFile() == null )
413 {
414 failures = true;
415 if ( result.getExceptions().isEmpty() )
416 {
417 Exception exception = new ArtifactNotFoundException( request.getArtifact(), null );
418 result.addException( exception );
419 }
420 RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
421 artifactResolved( session, trace, request.getArtifact(), null, result.getExceptions() );
422 }
423 }
424
425 if ( failures )
426 {
427 throw new ArtifactResolutionException( results );
428 }
429
430 return results;
431 }
432
433 private boolean isLocallyInstalled( LocalArtifactResult lar, VersionResult vr )
434 {
435 if ( lar.isAvailable() )
436 {
437 return true;
438 }
439 if ( lar.getFile() != null )
440 {
441 if ( vr.getRepository() instanceof LocalRepository )
442 {
443
444 return true;
445 }
446 else if ( vr.getRepository() == null && lar.getRequest().getRepositories().isEmpty() )
447 {
448
449 return true;
450 }
451 }
452 return false;
453 }
454
455 private File getFile( RepositorySystemSession session, Artifact artifact, File file )
456 throws ArtifactTransferException
457 {
458 if ( artifact.isSnapshot() && !artifact.getVersion().equals( artifact.getBaseVersion() )
459 && ConfigUtils.getBoolean( session, true, CONFIG_PROP_SNAPSHOT_NORMALIZATION ) )
460 {
461 String name = file.getName().replace( artifact.getVersion(), artifact.getBaseVersion() );
462 File dst = new File( file.getParent(), name );
463
464 boolean copy = dst.length() != file.length() || dst.lastModified() != file.lastModified();
465 if ( copy )
466 {
467 try
468 {
469 fileProcessor.copy( file, dst );
470 dst.setLastModified( file.lastModified() );
471 }
472 catch ( IOException e )
473 {
474 throw new ArtifactTransferException( artifact, null, e );
475 }
476 }
477
478 file = dst;
479 }
480
481 return file;
482 }
483
484 private void performDownloads( RepositorySystemSession session, ResolutionGroup group )
485 {
486 List<ArtifactDownload> downloads = gatherDownloads( session, group );
487 if ( downloads.isEmpty() )
488 {
489 return;
490 }
491
492 for ( ArtifactDownload download : downloads )
493 {
494 artifactDownloading( session, download.getTrace(), download.getArtifact(), group.repository );
495 }
496
497 try
498 {
499 RemoteRepository repo = group.repository;
500 if ( repo.isBlocked() )
501 {
502 if ( repo.getMirroredRepositories().isEmpty() )
503 {
504 throw new NoRepositoryConnectorException( repo, "Blocked repository: " + repo );
505 }
506 else
507 {
508 throw new NoRepositoryConnectorException( repo, "Blocked mirror for repositories: "
509 + repo.getMirroredRepositories() );
510 }
511 }
512
513 try ( RepositoryConnector connector =
514 repositoryConnectorProvider.newRepositoryConnector( session, group.repository ) )
515 {
516 connector.get( downloads, null );
517 }
518 }
519 catch ( NoRepositoryConnectorException e )
520 {
521 for ( ArtifactDownload download : downloads )
522 {
523 download.setException( new ArtifactTransferException( download.getArtifact(), group.repository, e ) );
524 }
525 }
526
527 evaluateDownloads( session, group );
528 }
529
530 private List<ArtifactDownload> gatherDownloads( RepositorySystemSession session, ResolutionGroup group )
531 {
532 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
533 List<ArtifactDownload> downloads = new ArrayList<>();
534
535 for ( ResolutionItem item : group.items )
536 {
537 Artifact artifact = item.artifact;
538
539 if ( item.resolved.get() )
540 {
541
542 continue;
543 }
544
545 ArtifactDownload download = new ArtifactDownload();
546 download.setArtifact( artifact );
547 download.setRequestContext( item.request.getRequestContext() );
548 download.setListener( SafeTransferListener.wrap( session ) );
549 download.setTrace( item.trace );
550 if ( item.local.getFile() != null )
551 {
552 download.setFile( item.local.getFile() );
553 download.setExistenceCheck( true );
554 }
555 else
556 {
557 String path =
558 lrm.getPathForRemoteArtifact( artifact, group.repository, item.request.getRequestContext() );
559 download.setFile( new File( lrm.getRepository().getBasedir(), path ) );
560 }
561
562 boolean snapshot = artifact.isSnapshot();
563 RepositoryPolicy policy =
564 remoteRepositoryManager.getPolicy( session, group.repository, !snapshot, snapshot );
565
566 int errorPolicy = Utils.getPolicy( session, artifact, group.repository );
567 if ( ( errorPolicy & ResolutionErrorPolicy.CACHE_ALL ) != 0 )
568 {
569 UpdateCheck<Artifact, ArtifactTransferException> check = new UpdateCheck<>();
570 check.setItem( artifact );
571 check.setFile( download.getFile() );
572 check.setFileValid( false );
573 check.setRepository( group.repository );
574 check.setPolicy( policy.getUpdatePolicy() );
575 item.updateCheck = check;
576 updateCheckManager.checkArtifact( session, check );
577 if ( !check.isRequired() )
578 {
579 item.result.addException( check.getException() );
580 continue;
581 }
582 }
583
584 download.setChecksumPolicy( policy.getChecksumPolicy() );
585 download.setRepositories( item.repository.getMirroredRepositories() );
586 downloads.add( download );
587 item.download = download;
588 }
589
590 return downloads;
591 }
592
593 private void evaluateDownloads( RepositorySystemSession session, ResolutionGroup group )
594 {
595 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
596
597 for ( ResolutionItem item : group.items )
598 {
599 ArtifactDownload download = item.download;
600 if ( download == null )
601 {
602 continue;
603 }
604
605 Artifact artifact = download.getArtifact();
606 if ( download.getException() == null )
607 {
608 item.resolved.set( true );
609 item.result.setRepository( group.repository );
610 try
611 {
612 artifact = artifact.setFile( getFile( session, artifact, download.getFile() ) );
613 item.result.setArtifact( artifact );
614
615 lrm.add( session, new LocalArtifactRegistration(
616 artifact, group.repository, download.getSupportedContexts() ) );
617 }
618 catch ( ArtifactTransferException e )
619 {
620 download.setException( e );
621 item.result.addException( e );
622 }
623 }
624 else
625 {
626 item.result.addException( download.getException() );
627 }
628
629
630
631
632
633 if ( item.updateCheck != null )
634 {
635 item.updateCheck.setException( download.getException() );
636 updateCheckManager.touchArtifact( session, item.updateCheck );
637 }
638
639 artifactDownloaded( session, download.getTrace(), artifact, group.repository, download.getException() );
640 if ( download.getException() == null )
641 {
642 artifactResolved( session, download.getTrace(), artifact, group.repository, null );
643 }
644 }
645 }
646
647 private void artifactResolving( RepositorySystemSession session, RequestTrace trace, Artifact artifact )
648 {
649 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_RESOLVING );
650 event.setTrace( trace );
651 event.setArtifact( artifact );
652
653 repositoryEventDispatcher.dispatch( event.build() );
654 }
655
656 private void artifactResolved( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
657 ArtifactRepository repository, List<Exception> exceptions )
658 {
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 {
666 event.setFile( artifact.getFile() );
667 }
668
669 repositoryEventDispatcher.dispatch( event.build() );
670 }
671
672 private void artifactDownloading( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
673 RemoteRepository repository )
674 {
675 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DOWNLOADING );
676 event.setTrace( trace );
677 event.setArtifact( artifact );
678 event.setRepository( repository );
679
680 repositoryEventDispatcher.dispatch( event.build() );
681 }
682
683 private void artifactDownloaded( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
684 RemoteRepository repository, Exception exception )
685 {
686 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DOWNLOADED );
687 event.setTrace( trace );
688 event.setArtifact( artifact );
689 event.setRepository( repository );
690 event.setException( exception );
691 if ( artifact != null )
692 {
693 event.setFile( artifact.getFile() );
694 }
695
696 repositoryEventDispatcher.dispatch( event.build() );
697 }
698
699 static class ResolutionGroup
700 {
701
702 final RemoteRepository repository;
703
704 final List<ResolutionItem> items = new ArrayList<>();
705
706 ResolutionGroup( RemoteRepository repository )
707 {
708 this.repository = repository;
709 }
710
711 boolean matches( RemoteRepository repo )
712 {
713 return repository.getUrl().equals( repo.getUrl() )
714 && repository.getContentType().equals( repo.getContentType() )
715 && repository.isRepositoryManager() == repo.isRepositoryManager();
716 }
717
718 }
719
720 static class ResolutionItem
721 {
722
723 final RequestTrace trace;
724
725 final ArtifactRequest request;
726
727 final ArtifactResult result;
728
729 final LocalArtifactResult local;
730
731 final RemoteRepository repository;
732
733 final Artifact artifact;
734
735 final AtomicBoolean resolved;
736
737 ArtifactDownload download;
738
739 UpdateCheck<Artifact, ArtifactTransferException> updateCheck;
740
741 ResolutionItem( RequestTrace trace, Artifact artifact, AtomicBoolean resolved, ArtifactResult result,
742 LocalArtifactResult local, RemoteRepository repository )
743 {
744 this.trace = trace;
745 this.artifact = artifact;
746 this.resolved = resolved;
747 this.result = result;
748 this.request = result.getRequest();
749 this.local = local;
750 this.repository = repository;
751 }
752
753 }
754
755 }