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