1 package org.apache.maven.artifact.repository.metadata;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.metadata.ArtifactMetadata;
23 import org.apache.maven.artifact.repository.ArtifactRepository;
24 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
25 import org.apache.maven.artifact.repository.DefaultRepositoryRequest;
26 import org.apache.maven.artifact.repository.RepositoryRequest;
27 import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader;
28 import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
29 import org.apache.maven.repository.legacy.UpdateCheckManager;
30 import org.apache.maven.repository.legacy.WagonManager;
31 import org.apache.maven.wagon.ResourceDoesNotExistException;
32 import org.apache.maven.wagon.TransferFailedException;
33 import org.codehaus.plexus.component.annotations.Component;
34 import org.codehaus.plexus.component.annotations.Requirement;
35 import org.codehaus.plexus.logging.AbstractLogEnabled;
36 import org.codehaus.plexus.util.ReaderFactory;
37 import org.codehaus.plexus.util.WriterFactory;
38 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
39
40 import java.io.File;
41 import java.io.FileNotFoundException;
42 import java.io.IOException;
43 import java.io.Reader;
44 import java.io.Writer;
45 import java.util.Date;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49
50
51
52
53 @Component( role = RepositoryMetadataManager.class )
54 public class DefaultRepositoryMetadataManager
55 extends AbstractLogEnabled
56 implements RepositoryMetadataManager
57 {
58 @Requirement
59 private WagonManager wagonManager;
60
61 @Requirement
62 private UpdateCheckManager updateCheckManager;
63
64 public void resolve( RepositoryMetadata metadata, List<ArtifactRepository> remoteRepositories,
65 ArtifactRepository localRepository )
66 throws RepositoryMetadataResolutionException
67 {
68 RepositoryRequest request = new DefaultRepositoryRequest();
69 request.setLocalRepository( localRepository );
70 request.setRemoteRepositories( remoteRepositories );
71 resolve( metadata, request );
72 }
73
74 public void resolve( RepositoryMetadata metadata, RepositoryRequest request )
75 throws RepositoryMetadataResolutionException
76 {
77 ArtifactRepository localRepo = request.getLocalRepository();
78 List<ArtifactRepository> remoteRepositories = request.getRemoteRepositories();
79
80 if ( !request.isOffline() )
81 {
82 Date localCopyLastModified = null;
83 if ( metadata.getBaseVersion() != null )
84 {
85 localCopyLastModified = getLocalCopyLastModified( localRepo, metadata );
86 }
87
88 for ( ArtifactRepository repository : remoteRepositories )
89 {
90 ArtifactRepositoryPolicy policy = metadata.getPolicy( repository );
91
92 File file =
93 new File( localRepo.getBasedir(), localRepo.pathOfLocalRepositoryMetadata( metadata, repository ) );
94 boolean update;
95
96 if ( !policy.isEnabled() )
97 {
98 update = false;
99
100 if ( getLogger().isDebugEnabled() )
101 {
102 getLogger().debug( "Skipping update check for " + metadata.getKey() + " (" + file
103 + ") from disabled repository " + repository.getId() + " ("
104 + repository.getUrl() + ")" );
105 }
106 }
107 else if ( request.isForceUpdate() )
108 {
109 update = true;
110 }
111 else if ( localCopyLastModified != null && !policy.checkOutOfDate( localCopyLastModified ) )
112 {
113 update = false;
114
115 if ( getLogger().isDebugEnabled() )
116 {
117 getLogger().debug(
118 "Skipping update check for " + metadata.getKey() + " (" + file + ") from repository "
119 + repository.getId() + " (" + repository.getUrl() + ") in favor of local copy" );
120 }
121 }
122 else
123 {
124 update = updateCheckManager.isUpdateRequired( metadata, repository, file );
125 }
126
127 if ( update )
128 {
129 getLogger().info( metadata.getKey() + ": checking for updates from " + repository.getId() );
130 try
131 {
132 wagonManager.getArtifactMetadata( metadata, repository, file, policy.getChecksumPolicy() );
133 }
134 catch ( ResourceDoesNotExistException e )
135 {
136 getLogger().debug( metadata + " could not be found on repository: " + repository.getId() );
137
138
139 if ( file.exists() )
140 {
141 if ( !file.delete() )
142 {
143
144 try
145 {
146 Thread.sleep( 10 );
147 }
148 catch ( InterruptedException ie )
149 {
150
151 }
152 file.delete();
153 }
154 }
155 }
156 catch ( TransferFailedException e )
157 {
158 getLogger().warn( metadata + " could not be retrieved from repository: " + repository.getId()
159 + " due to an error: " + e.getMessage() );
160 getLogger().debug( "Exception", e );
161 }
162 finally
163 {
164 updateCheckManager.touch( metadata, repository, file );
165 }
166 }
167
168
169
170 if ( file.exists() )
171 {
172 file.setLastModified( System.currentTimeMillis() );
173 }
174 }
175 }
176
177 try
178 {
179 mergeMetadata( metadata, remoteRepositories, localRepo );
180 }
181 catch ( RepositoryMetadataStoreException e )
182 {
183 throw new RepositoryMetadataResolutionException(
184 "Unable to store local copy of metadata: " + e.getMessage(), e );
185 }
186 }
187
188 private Date getLocalCopyLastModified( ArtifactRepository localRepository, RepositoryMetadata metadata )
189 {
190 String metadataPath = localRepository.pathOfLocalRepositoryMetadata( metadata, localRepository );
191 File metadataFile = new File( localRepository.getBasedir(), metadataPath );
192 return metadataFile.isFile() ? new Date( metadataFile.lastModified() ) : null;
193 }
194
195 private void mergeMetadata( RepositoryMetadata metadata, List<ArtifactRepository> remoteRepositories,
196 ArtifactRepository localRepository )
197 throws RepositoryMetadataStoreException
198 {
199
200
201
202
203
204 Map<ArtifactRepository, Metadata> previousMetadata = new HashMap<>();
205 ArtifactRepository selected = null;
206 for ( ArtifactRepository repository : remoteRepositories )
207 {
208 ArtifactRepositoryPolicy policy = metadata.getPolicy( repository );
209
210 if ( policy.isEnabled() && loadMetadata( metadata, repository, localRepository, previousMetadata ) )
211 {
212 metadata.setRepository( repository );
213 selected = repository;
214 }
215 }
216 if ( loadMetadata( metadata, localRepository, localRepository, previousMetadata ) )
217 {
218 metadata.setRepository( null );
219 selected = localRepository;
220 }
221
222 updateSnapshotMetadata( metadata, previousMetadata, selected, localRepository );
223 }
224
225 private void updateSnapshotMetadata( RepositoryMetadata metadata,
226 Map<ArtifactRepository, Metadata> previousMetadata,
227 ArtifactRepository selected, ArtifactRepository localRepository )
228 throws RepositoryMetadataStoreException
229 {
230
231 if ( metadata.isSnapshot() )
232 {
233 Metadata prevMetadata = metadata.getMetadata();
234
235 for ( ArtifactRepository repository : previousMetadata.keySet() )
236 {
237 Metadata m = previousMetadata.get( repository );
238 if ( repository.equals( selected ) )
239 {
240 if ( m.getVersioning() == null )
241 {
242 m.setVersioning( new Versioning() );
243 }
244
245 if ( m.getVersioning().getSnapshot() == null )
246 {
247 m.getVersioning().setSnapshot( new Snapshot() );
248 }
249 }
250 else
251 {
252 if ( ( m.getVersioning() != null ) && ( m.getVersioning().getSnapshot() != null )
253 && m.getVersioning().getSnapshot().isLocalCopy() )
254 {
255 m.getVersioning().getSnapshot().setLocalCopy( false );
256 metadata.setMetadata( m );
257 metadata.storeInLocalRepository( localRepository, repository );
258 }
259 }
260 }
261
262 metadata.setMetadata( prevMetadata );
263 }
264 }
265
266 private boolean loadMetadata( RepositoryMetadata repoMetadata, ArtifactRepository remoteRepository,
267 ArtifactRepository localRepository,
268 Map<ArtifactRepository, Metadata> previousMetadata )
269 {
270 boolean setRepository = false;
271
272 File metadataFile = new File( localRepository.getBasedir(),
273 localRepository.pathOfLocalRepositoryMetadata( repoMetadata, remoteRepository ) );
274
275 if ( metadataFile.exists() )
276 {
277 Metadata metadata;
278
279 try
280 {
281 metadata = readMetadata( metadataFile );
282 }
283 catch ( RepositoryMetadataReadException e )
284 {
285 if ( getLogger().isDebugEnabled() )
286 {
287 getLogger().warn( e.getMessage(), e );
288 }
289 else
290 {
291 getLogger().warn( e.getMessage() );
292 }
293 return setRepository;
294 }
295
296 if ( repoMetadata.isSnapshot() && ( previousMetadata != null ) )
297 {
298 previousMetadata.put( remoteRepository, metadata );
299 }
300
301 if ( repoMetadata.getMetadata() != null )
302 {
303 setRepository = repoMetadata.getMetadata().merge( metadata );
304 }
305 else
306 {
307 repoMetadata.setMetadata( metadata );
308 setRepository = true;
309 }
310 }
311 return setRepository;
312 }
313
314
315
316
317 protected Metadata readMetadata( File mappingFile )
318 throws RepositoryMetadataReadException
319 {
320 Metadata result;
321
322 try ( Reader reader = ReaderFactory.newXmlReader( mappingFile ) )
323 {
324 MetadataXpp3Reader mappingReader = new MetadataXpp3Reader();
325
326 result = mappingReader.read( reader, false );
327 }
328 catch ( FileNotFoundException e )
329 {
330 throw new RepositoryMetadataReadException( "Cannot read metadata from '" + mappingFile + "'", e );
331 }
332 catch ( IOException | XmlPullParserException e )
333 {
334 throw new RepositoryMetadataReadException(
335 "Cannot read metadata from '" + mappingFile + "': " + e.getMessage(), e );
336 }
337 return result;
338 }
339
340
341
342
343
344 private void fixTimestamp( File metadataFile, Metadata metadata, Metadata reference )
345 {
346 boolean changed = false;
347
348 if ( metadata != null && reference != null )
349 {
350 Versioning versioning = metadata.getVersioning();
351 Versioning versioningRef = reference.getVersioning();
352 if ( versioning != null && versioningRef != null )
353 {
354 String lastUpdated = versioning.getLastUpdated();
355 String now = versioningRef.getLastUpdated();
356 if ( lastUpdated != null && now != null && now.compareTo( lastUpdated ) < 0 )
357 {
358 getLogger().warn(
359 "The last updated timestamp in " + metadataFile + " refers to the future (now = " + now
360 + ", lastUpdated = " + lastUpdated + "). Please verify that the clocks of all"
361 + " deploying machines are reasonably synchronized." );
362 versioning.setLastUpdated( now );
363 changed = true;
364 }
365 }
366 }
367
368 if ( changed )
369 {
370 getLogger().debug( "Repairing metadata in " + metadataFile );
371
372 try ( Writer writer = WriterFactory.newXmlWriter( metadataFile ) )
373 {
374 new MetadataXpp3Writer().write( writer, metadata );
375 }
376 catch ( IOException e )
377 {
378 String msg = "Could not write fixed metadata to " + metadataFile + ": " + e.getMessage();
379 if ( getLogger().isDebugEnabled() )
380 {
381 getLogger().warn( msg, e );
382 }
383 else
384 {
385 getLogger().warn( msg );
386 }
387 }
388 }
389 }
390
391 public void resolveAlways( RepositoryMetadata metadata, ArtifactRepository localRepository,
392 ArtifactRepository remoteRepository )
393 throws RepositoryMetadataResolutionException
394 {
395 File file;
396 try
397 {
398 file = getArtifactMetadataFromDeploymentRepository( metadata, localRepository, remoteRepository );
399 }
400 catch ( TransferFailedException e )
401 {
402 throw new RepositoryMetadataResolutionException(
403 metadata + " could not be retrieved from repository: " + remoteRepository.getId() + " due to an error: "
404 + e.getMessage(), e );
405 }
406
407 try
408 {
409 if ( file.exists() )
410 {
411 Metadata prevMetadata = readMetadata( file );
412 metadata.setMetadata( prevMetadata );
413 }
414 }
415 catch ( RepositoryMetadataReadException e )
416 {
417 throw new RepositoryMetadataResolutionException( e.getMessage(), e );
418 }
419 }
420
421 private File getArtifactMetadataFromDeploymentRepository( ArtifactMetadata metadata, ArtifactRepository localRepo,
422 ArtifactRepository remoteRepository )
423 throws TransferFailedException
424 {
425 File file =
426 new File( localRepo.getBasedir(), localRepo.pathOfLocalRepositoryMetadata( metadata, remoteRepository ) );
427
428 try
429 {
430 wagonManager.getArtifactMetadataFromDeploymentRepository( metadata, remoteRepository, file,
431 ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN );
432 }
433 catch ( ResourceDoesNotExistException e )
434 {
435 getLogger().info(
436 metadata + " could not be found on repository: " + remoteRepository.getId() + ", so will be created" );
437
438
439 if ( file.exists() )
440 {
441 if ( !file.delete() )
442 {
443
444 try
445 {
446 Thread.sleep( 10 );
447 }
448 catch ( InterruptedException ie )
449 {
450
451 }
452 file.delete();
453 }
454 }
455 }
456 finally
457 {
458 if ( metadata instanceof RepositoryMetadata )
459 {
460 updateCheckManager.touch( (RepositoryMetadata) metadata, remoteRepository, file );
461 }
462 }
463 return file;
464 }
465
466 public void deploy( ArtifactMetadata metadata, ArtifactRepository localRepository,
467 ArtifactRepository deploymentRepository )
468 throws RepositoryMetadataDeploymentException
469 {
470 File file;
471 if ( metadata instanceof RepositoryMetadata )
472 {
473 getLogger().info( "Retrieving previous metadata from " + deploymentRepository.getId() );
474 try
475 {
476 file = getArtifactMetadataFromDeploymentRepository( metadata, localRepository, deploymentRepository );
477 }
478 catch ( TransferFailedException e )
479 {
480 throw new RepositoryMetadataDeploymentException(
481 metadata + " could not be retrieved from repository: " + deploymentRepository.getId()
482 + " due to an error: " + e.getMessage(), e );
483 }
484
485 if ( file.isFile() )
486 {
487 try
488 {
489 fixTimestamp( file, readMetadata( file ), ( (RepositoryMetadata) metadata ).getMetadata() );
490 }
491 catch ( RepositoryMetadataReadException e )
492 {
493
494 }
495 }
496 }
497 else
498 {
499
500 file = new File( localRepository.getBasedir(),
501 localRepository.pathOfLocalRepositoryMetadata( metadata, deploymentRepository ) );
502 }
503
504 try
505 {
506 metadata.storeInLocalRepository( localRepository, deploymentRepository );
507 }
508 catch ( RepositoryMetadataStoreException e )
509 {
510 throw new RepositoryMetadataDeploymentException( "Error installing metadata: " + e.getMessage(), e );
511 }
512
513 try
514 {
515 wagonManager.putArtifactMetadata( file, metadata, deploymentRepository );
516 }
517 catch ( TransferFailedException e )
518 {
519 throw new RepositoryMetadataDeploymentException( "Error while deploying metadata: " + e.getMessage(), e );
520 }
521 }
522
523 public void install( ArtifactMetadata metadata, ArtifactRepository localRepository )
524 throws RepositoryMetadataInstallationException
525 {
526 try
527 {
528 metadata.storeInLocalRepository( localRepository, localRepository );
529 }
530 catch ( RepositoryMetadataStoreException e )
531 {
532 throw new RepositoryMetadataInstallationException( "Error installing metadata: " + e.getMessage(), e );
533 }
534 }
535
536 }