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