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