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