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