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 requireNonNull( session, "session cannot be null" );
210 requireNonNull( session, "session cannot be null" );
211
212 return resolveArtifacts( session, Collections.singleton( request ) ).get( 0 );
213 }
214
215 public List<ArtifactResult> resolveArtifacts( RepositorySystemSession session,
216 Collection<? extends ArtifactRequest> requests )
217 throws ArtifactResolutionException
218 {
219 requireNonNull( session, "session cannot be null" );
220 requireNonNull( session, "session cannot be null" );
221 try ( SyncContext syncContext = syncContextFactory.newInstance( session, false ) )
222 {
223 Collection<Artifact> artifacts = new ArrayList<>( requests.size() );
224 for ( ArtifactRequest request : requests )
225 {
226 if ( request.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) != null )
227 {
228 continue;
229 }
230 artifacts.add( request.getArtifact() );
231 }
232
233 syncContext.acquire( artifacts, null );
234
235 return resolve( session, requests );
236 }
237 }
238
239 @SuppressWarnings( "checkstyle:methodlength" )
240 private List<ArtifactResult> resolve( RepositorySystemSession session,
241 Collection<? extends ArtifactRequest> requests )
242 throws ArtifactResolutionException
243 {
244 List<ArtifactResult> results = new ArrayList<>( requests.size() );
245 boolean failures = false;
246
247 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
248 WorkspaceReader workspace = session.getWorkspaceReader();
249
250 List<ResolutionGroup> groups = new ArrayList<>();
251
252 for ( ArtifactRequest request : requests )
253 {
254 RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
255
256 ArtifactResult result = new ArtifactResult( request );
257 results.add( result );
258
259 Artifact artifact = request.getArtifact();
260 List<RemoteRepository> repos = request.getRepositories();
261
262 artifactResolving( session, trace, artifact );
263
264 String localPath = artifact.getProperty( ArtifactProperties.LOCAL_PATH, null );
265 if ( localPath != null )
266 {
267
268 File file = new File( localPath );
269 if ( !file.isFile() )
270 {
271 failures = true;
272 result.addException( new ArtifactNotFoundException( artifact, null ) );
273 }
274 else
275 {
276 artifact = artifact.setFile( file );
277 result.setArtifact( artifact );
278 artifactResolved( session, trace, artifact, null, result.getExceptions() );
279 }
280 continue;
281 }
282
283 VersionResult versionResult;
284 try
285 {
286 VersionRequest versionRequest = new VersionRequest( artifact, repos, request.getRequestContext() );
287 versionRequest.setTrace( trace );
288 versionResult = versionResolver.resolveVersion( session, versionRequest );
289 }
290 catch ( VersionResolutionException e )
291 {
292 result.addException( e );
293 continue;
294 }
295
296 artifact = artifact.setVersion( versionResult.getVersion() );
297
298 if ( versionResult.getRepository() != null )
299 {
300 if ( versionResult.getRepository() instanceof RemoteRepository )
301 {
302 repos = Collections.singletonList( (RemoteRepository) versionResult.getRepository() );
303 }
304 else
305 {
306 repos = Collections.emptyList();
307 }
308 }
309
310 if ( workspace != null )
311 {
312 File file = workspace.findArtifact( artifact );
313 if ( file != null )
314 {
315 artifact = artifact.setFile( file );
316 result.setArtifact( artifact );
317 result.setRepository( workspace.getRepository() );
318 artifactResolved( session, trace, artifact, result.getRepository(), null );
319 continue;
320 }
321 }
322
323 LocalArtifactResult local =
324 lrm.find( session, new LocalArtifactRequest( artifact, repos, request.getRequestContext() ) );
325 if ( isLocallyInstalled( local, versionResult ) )
326 {
327 if ( local.getRepository() != null )
328 {
329 result.setRepository( local.getRepository() );
330 }
331 else
332 {
333 result.setRepository( lrm.getRepository() );
334 }
335 try
336 {
337 artifact = artifact.setFile( getFile( session, artifact, local.getFile() ) );
338 result.setArtifact( artifact );
339 artifactResolved( session, trace, artifact, result.getRepository(), null );
340 }
341 catch ( ArtifactTransferException e )
342 {
343 result.addException( e );
344 }
345 if ( !local.isAvailable() )
346 {
347
348
349
350
351
352
353 lrm.add( session, new LocalArtifactRegistration( artifact ) );
354 }
355 continue;
356 }
357 else if ( local.getFile() != null )
358 {
359 LOGGER.debug( "Verifying availability of {} from {}", local.getFile(), repos );
360 }
361
362 LOGGER.debug( "Resolving artifact {} from {}", artifact, repos );
363 AtomicBoolean resolved = new AtomicBoolean( false );
364 Iterator<ResolutionGroup> groupIt = groups.iterator();
365 for ( RemoteRepository repo : repos )
366 {
367 if ( !repo.getPolicy( artifact.isSnapshot() ).isEnabled() )
368 {
369 continue;
370 }
371
372 try
373 {
374 Utils.checkOffline( session, offlineController, repo );
375 }
376 catch ( RepositoryOfflineException e )
377 {
378 Exception exception =
379 new ArtifactNotFoundException( artifact, repo, "Cannot access " + repo.getId() + " ("
380 + repo.getUrl() + ") in offline mode and the artifact " + artifact
381 + " has not been downloaded from it before.", e );
382 result.addException( exception );
383 continue;
384 }
385
386 ResolutionGroup group = null;
387 while ( groupIt.hasNext() )
388 {
389 ResolutionGroup t = groupIt.next();
390 if ( t.matches( repo ) )
391 {
392 group = t;
393 break;
394 }
395 }
396 if ( group == null )
397 {
398 group = new ResolutionGroup( repo );
399 groups.add( group );
400 groupIt = Collections.<ResolutionGroup>emptyList().iterator();
401 }
402 group.items.add( new ResolutionItem( trace, artifact, resolved, result, local, repo ) );
403 }
404 }
405
406 for ( ResolutionGroup group : groups )
407 {
408 performDownloads( session, group );
409 }
410
411 for ( ArtifactResult result : results )
412 {
413 ArtifactRequest request = result.getRequest();
414
415 Artifact artifact = result.getArtifact();
416 if ( artifact == null || artifact.getFile() == null )
417 {
418 failures = true;
419 if ( result.getExceptions().isEmpty() )
420 {
421 Exception exception = new ArtifactNotFoundException( request.getArtifact(), null );
422 result.addException( exception );
423 }
424 RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
425 artifactResolved( session, trace, request.getArtifact(), null, result.getExceptions() );
426 }
427 }
428
429 if ( failures )
430 {
431 throw new ArtifactResolutionException( results );
432 }
433
434 return results;
435 }
436
437 private boolean isLocallyInstalled( LocalArtifactResult lar, VersionResult vr )
438 {
439 if ( lar.isAvailable() )
440 {
441 return true;
442 }
443 if ( lar.getFile() != null )
444 {
445 if ( vr.getRepository() instanceof LocalRepository )
446 {
447
448 return true;
449 }
450 else if ( vr.getRepository() == null && lar.getRequest().getRepositories().isEmpty() )
451 {
452
453 return true;
454 }
455 }
456 return false;
457 }
458
459 private File getFile( RepositorySystemSession session, Artifact artifact, File file )
460 throws ArtifactTransferException
461 {
462 if ( artifact.isSnapshot() && !artifact.getVersion().equals( artifact.getBaseVersion() )
463 && ConfigUtils.getBoolean( session, true, CONFIG_PROP_SNAPSHOT_NORMALIZATION ) )
464 {
465 String name = file.getName().replace( artifact.getVersion(), artifact.getBaseVersion() );
466 File dst = new File( file.getParent(), name );
467
468 boolean copy = dst.length() != file.length() || dst.lastModified() != file.lastModified();
469 if ( copy )
470 {
471 try
472 {
473 fileProcessor.copy( file, dst );
474 dst.setLastModified( file.lastModified() );
475 }
476 catch ( IOException e )
477 {
478 throw new ArtifactTransferException( artifact, null, e );
479 }
480 }
481
482 file = dst;
483 }
484
485 return file;
486 }
487
488 private void performDownloads( RepositorySystemSession session, ResolutionGroup group )
489 {
490 List<ArtifactDownload> downloads = gatherDownloads( session, group );
491 if ( downloads.isEmpty() )
492 {
493 return;
494 }
495
496 for ( ArtifactDownload download : downloads )
497 {
498 artifactDownloading( session, download.getTrace(), download.getArtifact(), group.repository );
499 }
500
501 try
502 {
503 RemoteRepository repo = group.repository;
504 if ( repo.isBlocked() )
505 {
506 if ( repo.getMirroredRepositories().isEmpty() )
507 {
508 throw new NoRepositoryConnectorException( repo, "Blocked repository: " + repo );
509 }
510 else
511 {
512 throw new NoRepositoryConnectorException( repo, "Blocked mirror for repositories: "
513 + repo.getMirroredRepositories() );
514 }
515 }
516
517 try ( RepositoryConnector connector =
518 repositoryConnectorProvider.newRepositoryConnector( session, group.repository ) )
519 {
520 connector.get( downloads, null );
521 }
522 }
523 catch ( NoRepositoryConnectorException e )
524 {
525 for ( ArtifactDownload download : downloads )
526 {
527 download.setException( new ArtifactTransferException( download.getArtifact(), group.repository, e ) );
528 }
529 }
530
531 evaluateDownloads( session, group );
532 }
533
534 private List<ArtifactDownload> gatherDownloads( RepositorySystemSession session, ResolutionGroup group )
535 {
536 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
537 List<ArtifactDownload> downloads = new ArrayList<>();
538
539 for ( ResolutionItem item : group.items )
540 {
541 Artifact artifact = item.artifact;
542
543 if ( item.resolved.get() )
544 {
545
546 continue;
547 }
548
549 ArtifactDownload download = new ArtifactDownload();
550 download.setArtifact( artifact );
551 download.setRequestContext( item.request.getRequestContext() );
552 download.setListener( SafeTransferListener.wrap( session ) );
553 download.setTrace( item.trace );
554 if ( item.local.getFile() != null )
555 {
556 download.setFile( item.local.getFile() );
557 download.setExistenceCheck( true );
558 }
559 else
560 {
561 String path =
562 lrm.getPathForRemoteArtifact( artifact, group.repository, item.request.getRequestContext() );
563 download.setFile( new File( lrm.getRepository().getBasedir(), path ) );
564 }
565
566 boolean snapshot = artifact.isSnapshot();
567 RepositoryPolicy policy =
568 remoteRepositoryManager.getPolicy( session, group.repository, !snapshot, snapshot );
569
570 int errorPolicy = Utils.getPolicy( session, artifact, group.repository );
571 if ( ( errorPolicy & ResolutionErrorPolicy.CACHE_ALL ) != 0 )
572 {
573 UpdateCheck<Artifact, ArtifactTransferException> check = new UpdateCheck<>();
574 check.setItem( artifact );
575 check.setFile( download.getFile() );
576 check.setFileValid( false );
577 check.setRepository( group.repository );
578 check.setPolicy( policy.getUpdatePolicy() );
579 item.updateCheck = check;
580 updateCheckManager.checkArtifact( session, check );
581 if ( !check.isRequired() )
582 {
583 item.result.addException( check.getException() );
584 continue;
585 }
586 }
587
588 download.setChecksumPolicy( policy.getChecksumPolicy() );
589 download.setRepositories( item.repository.getMirroredRepositories() );
590 downloads.add( download );
591 item.download = download;
592 }
593
594 return downloads;
595 }
596
597 private void evaluateDownloads( RepositorySystemSession session, ResolutionGroup group )
598 {
599 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
600
601 for ( ResolutionItem item : group.items )
602 {
603 ArtifactDownload download = item.download;
604 if ( download == null )
605 {
606 continue;
607 }
608
609 Artifact artifact = download.getArtifact();
610 if ( download.getException() == null )
611 {
612 item.resolved.set( true );
613 item.result.setRepository( group.repository );
614 try
615 {
616 artifact = artifact.setFile( getFile( session, artifact, download.getFile() ) );
617 item.result.setArtifact( artifact );
618
619 lrm.add( session, new LocalArtifactRegistration(
620 artifact, group.repository, download.getSupportedContexts() ) );
621 }
622 catch ( ArtifactTransferException e )
623 {
624 download.setException( e );
625 item.result.addException( e );
626 }
627 }
628 else
629 {
630 item.result.addException( download.getException() );
631 }
632
633
634
635
636
637 if ( item.updateCheck != null )
638 {
639 item.updateCheck.setException( download.getException() );
640 updateCheckManager.touchArtifact( session, item.updateCheck );
641 }
642
643 artifactDownloaded( session, download.getTrace(), artifact, group.repository, download.getException() );
644 if ( download.getException() == null )
645 {
646 artifactResolved( session, download.getTrace(), artifact, group.repository, null );
647 }
648 }
649 }
650
651 private void artifactResolving( RepositorySystemSession session, RequestTrace trace, Artifact artifact )
652 {
653 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_RESOLVING );
654 event.setTrace( trace );
655 event.setArtifact( artifact );
656
657 repositoryEventDispatcher.dispatch( event.build() );
658 }
659
660 private void artifactResolved( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
661 ArtifactRepository repository, List<Exception> exceptions )
662 {
663 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_RESOLVED );
664 event.setTrace( trace );
665 event.setArtifact( artifact );
666 event.setRepository( repository );
667 event.setExceptions( exceptions );
668 if ( artifact != null )
669 {
670 event.setFile( artifact.getFile() );
671 }
672
673 repositoryEventDispatcher.dispatch( event.build() );
674 }
675
676 private void artifactDownloading( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
677 RemoteRepository repository )
678 {
679 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DOWNLOADING );
680 event.setTrace( trace );
681 event.setArtifact( artifact );
682 event.setRepository( repository );
683
684 repositoryEventDispatcher.dispatch( event.build() );
685 }
686
687 private void artifactDownloaded( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
688 RemoteRepository repository, Exception exception )
689 {
690 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DOWNLOADED );
691 event.setTrace( trace );
692 event.setArtifact( artifact );
693 event.setRepository( repository );
694 event.setException( exception );
695 if ( artifact != null )
696 {
697 event.setFile( artifact.getFile() );
698 }
699
700 repositoryEventDispatcher.dispatch( event.build() );
701 }
702
703 static class ResolutionGroup
704 {
705
706 final RemoteRepository repository;
707
708 final List<ResolutionItem> items = new ArrayList<>();
709
710 ResolutionGroup( RemoteRepository repository )
711 {
712 this.repository = repository;
713 }
714
715 boolean matches( RemoteRepository repo )
716 {
717 return repository.getUrl().equals( repo.getUrl() )
718 && repository.getContentType().equals( repo.getContentType() )
719 && repository.isRepositoryManager() == repo.isRepositoryManager();
720 }
721
722 }
723
724 static class ResolutionItem
725 {
726
727 final RequestTrace trace;
728
729 final ArtifactRequest request;
730
731 final ArtifactResult result;
732
733 final LocalArtifactResult local;
734
735 final RemoteRepository repository;
736
737 final Artifact artifact;
738
739 final AtomicBoolean resolved;
740
741 ArtifactDownload download;
742
743 UpdateCheck<Artifact, ArtifactTransferException> updateCheck;
744
745 ResolutionItem( RequestTrace trace, Artifact artifact, AtomicBoolean resolved, ArtifactResult result,
746 LocalArtifactResult local, RemoteRepository repository )
747 {
748 this.trace = trace;
749 this.artifact = artifact;
750 this.resolved = resolved;
751 this.result = result;
752 this.request = result.getRequest();
753 this.local = local;
754 this.repository = repository;
755 }
756
757 }
758
759 }