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.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import static java.util.Objects.requireNonNull;
30 import java.util.concurrent.Executor;
31 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.LinkedBlockingQueue;
33 import java.util.concurrent.ThreadPoolExecutor;
34 import java.util.concurrent.TimeUnit;
35
36 import javax.inject.Inject;
37 import javax.inject.Named;
38 import javax.inject.Singleton;
39
40 import org.eclipse.aether.RepositoryEvent;
41 import org.eclipse.aether.RepositoryEvent.EventType;
42 import org.eclipse.aether.RepositorySystemSession;
43 import org.eclipse.aether.RequestTrace;
44 import org.eclipse.aether.SyncContext;
45 import org.eclipse.aether.impl.MetadataResolver;
46 import org.eclipse.aether.impl.OfflineController;
47 import org.eclipse.aether.impl.RemoteRepositoryFilterManager;
48 import org.eclipse.aether.impl.RemoteRepositoryManager;
49 import org.eclipse.aether.impl.RepositoryConnectorProvider;
50 import org.eclipse.aether.impl.RepositoryEventDispatcher;
51 import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
52 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
53 import org.eclipse.aether.impl.UpdateCheck;
54 import org.eclipse.aether.impl.UpdateCheckManager;
55 import org.eclipse.aether.metadata.Metadata;
56 import org.eclipse.aether.repository.ArtifactRepository;
57 import org.eclipse.aether.repository.LocalMetadataRegistration;
58 import org.eclipse.aether.repository.LocalMetadataRequest;
59 import org.eclipse.aether.repository.LocalMetadataResult;
60 import org.eclipse.aether.repository.LocalRepository;
61 import org.eclipse.aether.repository.LocalRepositoryManager;
62 import org.eclipse.aether.repository.RemoteRepository;
63 import org.eclipse.aether.repository.RepositoryPolicy;
64 import org.eclipse.aether.resolution.MetadataRequest;
65 import org.eclipse.aether.resolution.MetadataResult;
66 import org.eclipse.aether.spi.connector.MetadataDownload;
67 import org.eclipse.aether.spi.connector.RepositoryConnector;
68 import org.eclipse.aether.spi.locator.Service;
69 import org.eclipse.aether.spi.locator.ServiceLocator;
70 import org.eclipse.aether.transfer.MetadataNotFoundException;
71 import org.eclipse.aether.transfer.MetadataTransferException;
72 import org.eclipse.aether.transfer.NoRepositoryConnectorException;
73 import org.eclipse.aether.transfer.RepositoryOfflineException;
74 import org.eclipse.aether.util.ConfigUtils;
75 import org.eclipse.aether.util.concurrency.RunnableErrorForwarder;
76 import org.eclipse.aether.util.concurrency.WorkerThreadFactory;
77
78
79
80 @Singleton
81 @Named
82 public class DefaultMetadataResolver
83 implements MetadataResolver, Service
84 {
85
86 private static final String CONFIG_PROP_THREADS = "aether.metadataResolver.threads";
87
88 private RepositoryEventDispatcher repositoryEventDispatcher;
89
90 private UpdateCheckManager updateCheckManager;
91
92 private RepositoryConnectorProvider repositoryConnectorProvider;
93
94 private RemoteRepositoryManager remoteRepositoryManager;
95
96 private SyncContextFactory syncContextFactory;
97
98 private OfflineController offlineController;
99
100 private RemoteRepositoryFilterManager remoteRepositoryFilterManager;
101
102 public DefaultMetadataResolver()
103 {
104
105 }
106
107 @Inject
108 DefaultMetadataResolver( RepositoryEventDispatcher repositoryEventDispatcher,
109 UpdateCheckManager updateCheckManager,
110 RepositoryConnectorProvider repositoryConnectorProvider,
111 RemoteRepositoryManager remoteRepositoryManager, SyncContextFactory syncContextFactory,
112 OfflineController offlineController,
113 RemoteRepositoryFilterManager remoteRepositoryFilterManager )
114 {
115 setRepositoryEventDispatcher( repositoryEventDispatcher );
116 setUpdateCheckManager( updateCheckManager );
117 setRepositoryConnectorProvider( repositoryConnectorProvider );
118 setRemoteRepositoryManager( remoteRepositoryManager );
119 setSyncContextFactory( syncContextFactory );
120 setOfflineController( offlineController );
121 setRemoteRepositoryFilterManager( remoteRepositoryFilterManager );
122 }
123
124 public void initService( ServiceLocator locator )
125 {
126 setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
127 setUpdateCheckManager( locator.getService( UpdateCheckManager.class ) );
128 setRepositoryConnectorProvider( locator.getService( RepositoryConnectorProvider.class ) );
129 setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
130 setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
131 setOfflineController( locator.getService( OfflineController.class ) );
132 setRemoteRepositoryFilterManager( locator.getService( RemoteRepositoryFilterManager.class ) );
133 }
134
135 public DefaultMetadataResolver setRepositoryEventDispatcher( RepositoryEventDispatcher repositoryEventDispatcher )
136 {
137 this.repositoryEventDispatcher = requireNonNull(
138 repositoryEventDispatcher, "repository event dispatcher cannot be null" );
139 return this;
140 }
141
142 public DefaultMetadataResolver setUpdateCheckManager( UpdateCheckManager updateCheckManager )
143 {
144 this.updateCheckManager = requireNonNull( updateCheckManager, "update check manager cannot be null" );
145 return this;
146 }
147
148 public DefaultMetadataResolver setRepositoryConnectorProvider(
149 RepositoryConnectorProvider repositoryConnectorProvider )
150 {
151 this.repositoryConnectorProvider = requireNonNull(
152 repositoryConnectorProvider, "repository connector provider cannot be null" );
153 return this;
154 }
155
156 public DefaultMetadataResolver setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
157 {
158 this.remoteRepositoryManager = requireNonNull(
159 remoteRepositoryManager, "remote repository provider cannot be null" );
160 return this;
161 }
162
163 public DefaultMetadataResolver setSyncContextFactory( SyncContextFactory syncContextFactory )
164 {
165 this.syncContextFactory = requireNonNull( syncContextFactory, "sync context factory cannot be null" );
166 return this;
167 }
168
169 public DefaultMetadataResolver setOfflineController( OfflineController offlineController )
170 {
171 this.offlineController = requireNonNull( offlineController, "offline controller cannot be null" );
172 return this;
173 }
174
175 public DefaultMetadataResolver setRemoteRepositoryFilterManager(
176 RemoteRepositoryFilterManager remoteRepositoryFilterManager )
177 {
178 this.remoteRepositoryFilterManager = requireNonNull( remoteRepositoryFilterManager,
179 "remote repository filter manager cannot be null" );
180 return this;
181 }
182
183 public List<MetadataResult> resolveMetadata( RepositorySystemSession session,
184 Collection<? extends MetadataRequest> requests )
185 {
186 requireNonNull( session, "session cannot be null" );
187 requireNonNull( requests, "requests cannot be null" );
188 try ( SyncContext syncContext = syncContextFactory.newInstance( session, false ) )
189 {
190 Collection<Metadata> metadata = new ArrayList<>( requests.size() );
191 for ( MetadataRequest request : requests )
192 {
193 metadata.add( request.getMetadata() );
194 }
195
196 syncContext.acquire( null, metadata );
197
198 return resolve( session, requests );
199 }
200 }
201
202 @SuppressWarnings( "checkstyle:methodlength" )
203 private List<MetadataResult> resolve( RepositorySystemSession session,
204 Collection<? extends MetadataRequest> requests )
205 {
206 List<MetadataResult> results = new ArrayList<>( requests.size() );
207
208 List<ResolveTask> tasks = new ArrayList<>( requests.size() );
209
210 Map<File, Long> localLastUpdates = new HashMap<>();
211
212 RemoteRepositoryFilter remoteRepositoryFilter = remoteRepositoryFilterManager
213 .getRemoteRepositoryFilter( session );
214
215 for ( MetadataRequest request : requests )
216 {
217 RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
218
219 MetadataResult result = new MetadataResult( request );
220 results.add( result );
221
222 Metadata metadata = request.getMetadata();
223 RemoteRepository repository = request.getRepository();
224
225 if ( repository == null )
226 {
227 LocalRepository localRepo = session.getLocalRepositoryManager().getRepository();
228
229 metadataResolving( session, trace, metadata, localRepo );
230
231 File localFile = getLocalFile( session, metadata );
232
233 if ( localFile != null )
234 {
235 metadata = metadata.setFile( localFile );
236 result.setMetadata( metadata );
237 }
238 else
239 {
240 result.setException( new MetadataNotFoundException( metadata, localRepo ) );
241 }
242
243 metadataResolved( session, trace, metadata, localRepo, result.getException() );
244 continue;
245 }
246
247 if ( remoteRepositoryFilter != null )
248 {
249 RemoteRepositoryFilter.Result filterResult = remoteRepositoryFilter.acceptMetadata(
250 repository, metadata );
251 if ( !filterResult.isAccepted() )
252 {
253 result.setException(
254 new MetadataNotFoundException( metadata, repository, filterResult.reasoning() ) );
255 continue;
256 }
257 }
258
259 List<RemoteRepository> repositories = getEnabledSourceRepositories( repository, metadata.getNature() );
260
261 if ( repositories.isEmpty() )
262 {
263 continue;
264 }
265
266 metadataResolving( session, trace, metadata, repository );
267 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
268 LocalMetadataRequest localRequest =
269 new LocalMetadataRequest( metadata, repository, request.getRequestContext() );
270 LocalMetadataResult lrmResult = lrm.find( session, localRequest );
271
272 File metadataFile = lrmResult.getFile();
273
274 try
275 {
276 Utils.checkOffline( session, offlineController, repository );
277 }
278 catch ( RepositoryOfflineException e )
279 {
280 if ( metadataFile != null )
281 {
282 metadata = metadata.setFile( metadataFile );
283 result.setMetadata( metadata );
284 }
285 else
286 {
287 String msg =
288 "Cannot access " + repository.getId() + " (" + repository.getUrl()
289 + ") in offline mode and the metadata " + metadata
290 + " has not been downloaded from it before";
291 result.setException( new MetadataNotFoundException( metadata, repository, msg, e ) );
292 }
293
294 metadataResolved( session, trace, metadata, repository, result.getException() );
295 continue;
296 }
297
298 Long localLastUpdate = null;
299 if ( request.isFavorLocalRepository() )
300 {
301 File localFile = getLocalFile( session, metadata );
302 localLastUpdate = localLastUpdates.get( localFile );
303 if ( localLastUpdate == null )
304 {
305 localLastUpdate = localFile != null ? localFile.lastModified() : 0;
306 localLastUpdates.put( localFile, localLastUpdate );
307 }
308 }
309
310 List<UpdateCheck<Metadata, MetadataTransferException>> checks = new ArrayList<>();
311 Exception exception = null;
312 for ( RemoteRepository repo : repositories )
313 {
314 UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>();
315 check.setLocalLastUpdated( ( localLastUpdate != null ) ? localLastUpdate : 0 );
316 check.setItem( metadata );
317
318
319 File checkFile = new File(
320 session.getLocalRepository().getBasedir(),
321 session.getLocalRepositoryManager()
322 .getPathForRemoteMetadata( metadata, repository, request.getRequestContext() ) );
323 check.setFile( checkFile );
324 check.setRepository( repository );
325 check.setAuthoritativeRepository( repo );
326 check.setPolicy( getPolicy( session, repo, metadata.getNature() ).getUpdatePolicy() );
327
328 if ( lrmResult.isStale() )
329 {
330 checks.add( check );
331 }
332 else
333 {
334 updateCheckManager.checkMetadata( session, check );
335 if ( check.isRequired() )
336 {
337 checks.add( check );
338 }
339 else if ( exception == null )
340 {
341 exception = check.getException();
342 }
343 }
344 }
345
346 if ( !checks.isEmpty() )
347 {
348 RepositoryPolicy policy = getPolicy( session, repository, metadata.getNature() );
349
350
351 File installFile = new File(
352 session.getLocalRepository().getBasedir(),
353 session.getLocalRepositoryManager().getPathForRemoteMetadata(
354 metadata, request.getRepository(), request.getRequestContext() ) );
355
356 metadataDownloading(
357 session, trace, result.getRequest().getMetadata(), result.getRequest().getRepository() );
358
359 ResolveTask task =
360 new ResolveTask( session, trace, result, installFile, checks, policy.getChecksumPolicy() );
361 tasks.add( task );
362 }
363 else
364 {
365 result.setException( exception );
366 if ( metadataFile != null )
367 {
368 metadata = metadata.setFile( metadataFile );
369 result.setMetadata( metadata );
370 }
371 metadataResolved( session, trace, metadata, repository, result.getException() );
372 }
373 }
374
375 if ( !tasks.isEmpty() )
376 {
377 int threads = ConfigUtils.getInteger( session, 4, CONFIG_PROP_THREADS );
378 Executor executor = getExecutor( Math.min( tasks.size(), threads ) );
379 try
380 {
381 RunnableErrorForwarder errorForwarder = new RunnableErrorForwarder();
382
383 for ( ResolveTask task : tasks )
384 {
385 executor.execute( errorForwarder.wrap( task ) );
386 }
387
388 errorForwarder.await();
389
390 for ( ResolveTask task : tasks )
391 {
392
393
394
395
396
397 for ( UpdateCheck<Metadata, MetadataTransferException> check : task.checks )
398 {
399 updateCheckManager.touchMetadata( task.session, check.setException( task.exception ) );
400 }
401
402 metadataDownloaded( session, task.trace, task.request.getMetadata(), task.request.getRepository(),
403 task.metadataFile, task.exception );
404
405 task.result.setException( task.exception );
406 }
407 }
408 finally
409 {
410 shutdown( executor );
411 }
412 for ( ResolveTask task : tasks )
413 {
414 Metadata metadata = task.request.getMetadata();
415
416 LocalMetadataRequest localRequest = new LocalMetadataRequest(
417 metadata, task.request.getRepository(), task.request.getRequestContext() );
418 File metadataFile = session.getLocalRepositoryManager().find( session, localRequest ).getFile();
419 if ( metadataFile != null )
420 {
421 metadata = metadata.setFile( metadataFile );
422 task.result.setMetadata( metadata );
423 }
424 if ( task.result.getException() == null )
425 {
426 task.result.setUpdated( true );
427 }
428 metadataResolved( session, task.trace, metadata, task.request.getRepository(),
429 task.result.getException() );
430 }
431 }
432
433 return results;
434 }
435
436 private File getLocalFile( RepositorySystemSession session, Metadata metadata )
437 {
438 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
439 LocalMetadataResult localResult = lrm.find( session, new LocalMetadataRequest( metadata, null, null ) );
440 return localResult.getFile();
441 }
442
443 private List<RemoteRepository> getEnabledSourceRepositories( RemoteRepository repository, Metadata.Nature nature )
444 {
445 List<RemoteRepository> repositories = new ArrayList<>();
446
447 if ( repository.isRepositoryManager() )
448 {
449 for ( RemoteRepository repo : repository.getMirroredRepositories() )
450 {
451 if ( isEnabled( repo, nature ) )
452 {
453 repositories.add( repo );
454 }
455 }
456 }
457 else if ( isEnabled( repository, nature ) )
458 {
459 repositories.add( repository );
460 }
461
462 return repositories;
463 }
464
465 private boolean isEnabled( RemoteRepository repository, Metadata.Nature nature )
466 {
467 if ( !Metadata.Nature.SNAPSHOT.equals( nature ) && repository.getPolicy( false ).isEnabled() )
468 {
469 return true;
470 }
471 if ( !Metadata.Nature.RELEASE.equals( nature ) && repository.getPolicy( true ).isEnabled() )
472 {
473 return true;
474 }
475 return false;
476 }
477
478 private RepositoryPolicy getPolicy( RepositorySystemSession session, RemoteRepository repository,
479 Metadata.Nature nature )
480 {
481 boolean releases = !Metadata.Nature.SNAPSHOT.equals( nature );
482 boolean snapshots = !Metadata.Nature.RELEASE.equals( nature );
483 return remoteRepositoryManager.getPolicy( session, repository, releases, snapshots );
484 }
485
486 private void metadataResolving( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
487 ArtifactRepository repository )
488 {
489 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_RESOLVING );
490 event.setTrace( trace );
491 event.setMetadata( metadata );
492 event.setRepository( repository );
493
494 repositoryEventDispatcher.dispatch( event.build() );
495 }
496
497 private void metadataResolved( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
498 ArtifactRepository repository, Exception exception )
499 {
500 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_RESOLVED );
501 event.setTrace( trace );
502 event.setMetadata( metadata );
503 event.setRepository( repository );
504 event.setException( exception );
505 event.setFile( metadata.getFile() );
506
507 repositoryEventDispatcher.dispatch( event.build() );
508 }
509
510 private void metadataDownloading( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
511 ArtifactRepository repository )
512 {
513 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_DOWNLOADING );
514 event.setTrace( trace );
515 event.setMetadata( metadata );
516 event.setRepository( repository );
517
518 repositoryEventDispatcher.dispatch( event.build() );
519 }
520
521 private void metadataDownloaded( RepositorySystemSession session, RequestTrace trace, Metadata metadata,
522 ArtifactRepository repository, File file, Exception exception )
523 {
524 RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_DOWNLOADED );
525 event.setTrace( trace );
526 event.setMetadata( metadata );
527 event.setRepository( repository );
528 event.setException( exception );
529 event.setFile( file );
530
531 repositoryEventDispatcher.dispatch( event.build() );
532 }
533
534 private Executor getExecutor( int threads )
535 {
536 if ( threads <= 1 )
537 {
538 return command -> command.run();
539 }
540 else
541 {
542 return new ThreadPoolExecutor( threads, threads, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
543 new WorkerThreadFactory( null ) );
544 }
545 }
546
547 private void shutdown( Executor executor )
548 {
549 if ( executor instanceof ExecutorService )
550 {
551 ( (ExecutorService) executor ).shutdown();
552 }
553 }
554
555 class ResolveTask
556 implements Runnable
557 {
558 final RepositorySystemSession session;
559
560 final RequestTrace trace;
561
562 final MetadataResult result;
563
564 final MetadataRequest request;
565
566 final File metadataFile;
567
568 final String policy;
569
570 final List<UpdateCheck<Metadata, MetadataTransferException>> checks;
571
572 volatile MetadataTransferException exception;
573
574 ResolveTask( RepositorySystemSession session, RequestTrace trace, MetadataResult result,
575 File metadataFile, List<UpdateCheck<Metadata, MetadataTransferException>> checks,
576 String policy )
577 {
578 this.session = session;
579 this.trace = trace;
580 this.result = result;
581 this.request = result.getRequest();
582 this.metadataFile = metadataFile;
583 this.policy = policy;
584 this.checks = checks;
585 }
586
587 public void run()
588 {
589 Metadata metadata = request.getMetadata();
590 RemoteRepository requestRepository = request.getRepository();
591
592 try
593 {
594 List<RemoteRepository> repositories = new ArrayList<>();
595 for ( UpdateCheck<Metadata, MetadataTransferException> check : checks )
596 {
597 repositories.add( check.getAuthoritativeRepository() );
598 }
599
600 MetadataDownload download = new MetadataDownload();
601 download.setMetadata( metadata );
602 download.setRequestContext( request.getRequestContext() );
603 download.setFile( metadataFile );
604 download.setChecksumPolicy( policy );
605 download.setRepositories( repositories );
606 download.setListener( SafeTransferListener.wrap( session ) );
607 download.setTrace( trace );
608
609 try ( RepositoryConnector connector =
610 repositoryConnectorProvider.newRepositoryConnector( session, requestRepository ) )
611 {
612 connector.get( null, Collections.singletonList( download ) );
613 }
614
615 exception = download.getException();
616
617 if ( exception == null )
618 {
619
620 List<String> contexts = Collections.singletonList( request.getRequestContext() );
621 LocalMetadataRegistration registration =
622 new LocalMetadataRegistration( metadata, requestRepository, contexts );
623
624 session.getLocalRepositoryManager().add( session, registration );
625 }
626 else if ( request.isDeleteLocalCopyIfMissing() && exception instanceof MetadataNotFoundException )
627 {
628 download.getFile().delete();
629 }
630 }
631 catch ( NoRepositoryConnectorException e )
632 {
633 exception = new MetadataTransferException( metadata, requestRepository, e );
634 }
635 }
636 }
637 }