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