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