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 file.delete();
145 }
146 }
147 catch ( TransferFailedException e )
148 {
149 getLogger().warn( metadata + " could not be retrieved from repository: " + repository.getId()
150 + " due to an error: " + e.getMessage() );
151 getLogger().debug( "Exception", e );
152 }
153 finally
154 {
155 updateCheckManager.touch( metadata, repository, file );
156 }
157 }
158
159
160
161 if ( file.exists() )
162 {
163 file.setLastModified( System.currentTimeMillis() );
164 }
165 }
166 }
167
168 try
169 {
170 mergeMetadata( metadata, remoteRepositories, localRepo );
171 }
172 catch ( RepositoryMetadataStoreException e )
173 {
174 throw new RepositoryMetadataResolutionException( "Unable to store local copy of metadata: "
175 + e.getMessage(), e );
176 }
177 }
178
179 private Date getLocalCopyLastModified( ArtifactRepository localRepository, RepositoryMetadata metadata )
180 {
181 String metadataPath = localRepository.pathOfLocalRepositoryMetadata( metadata, localRepository );
182 File metadataFile = new File( localRepository.getBasedir(), metadataPath );
183 return metadataFile.isFile() ? new Date( metadataFile.lastModified() ) : null;
184 }
185
186 private void mergeMetadata( RepositoryMetadata metadata, List<ArtifactRepository> remoteRepositories,
187 ArtifactRepository localRepository )
188 throws RepositoryMetadataStoreException
189 {
190
191
192
193
194
195 Map<ArtifactRepository, Metadata> previousMetadata = new HashMap<ArtifactRepository, Metadata>();
196 ArtifactRepository selected = null;
197 for ( ArtifactRepository repository : remoteRepositories )
198 {
199 ArtifactRepositoryPolicy policy = metadata.getPolicy( repository );
200
201 if ( policy.isEnabled() && loadMetadata( metadata, repository, localRepository, previousMetadata ) )
202 {
203 metadata.setRepository( repository );
204 selected = repository;
205 }
206 }
207 if ( loadMetadata( metadata, localRepository, localRepository, previousMetadata ) )
208 {
209 metadata.setRepository( null );
210 selected = localRepository;
211 }
212
213 updateSnapshotMetadata( metadata, previousMetadata, selected, localRepository );
214 }
215
216 private void updateSnapshotMetadata( RepositoryMetadata metadata,
217 Map<ArtifactRepository, Metadata> previousMetadata,
218 ArtifactRepository selected, ArtifactRepository localRepository )
219 throws RepositoryMetadataStoreException
220 {
221
222 if ( metadata.isSnapshot() )
223 {
224 Metadata prevMetadata = metadata.getMetadata();
225
226 for ( ArtifactRepository repository : previousMetadata.keySet() )
227 {
228 Metadata m = previousMetadata.get( repository );
229 if ( repository.equals( selected ) )
230 {
231 if ( m.getVersioning() == null )
232 {
233 m.setVersioning( new Versioning() );
234 }
235
236 if ( m.getVersioning().getSnapshot() == null )
237 {
238 m.getVersioning().setSnapshot( new Snapshot() );
239 }
240 }
241 else
242 {
243 if ( ( m.getVersioning() != null ) && ( m.getVersioning().getSnapshot() != null )
244 && m.getVersioning().getSnapshot().isLocalCopy() )
245 {
246 m.getVersioning().getSnapshot().setLocalCopy( false );
247 metadata.setMetadata( m );
248 metadata.storeInLocalRepository( localRepository, repository );
249 }
250 }
251 }
252
253 metadata.setMetadata( prevMetadata );
254 }
255 }
256
257 private boolean loadMetadata( RepositoryMetadata repoMetadata, ArtifactRepository remoteRepository,
258 ArtifactRepository localRepository, Map<ArtifactRepository,
259 Metadata> previousMetadata )
260 {
261 boolean setRepository = false;
262
263 File metadataFile =
264 new File( localRepository.getBasedir(), localRepository.pathOfLocalRepositoryMetadata( repoMetadata,
265 remoteRepository ) );
266
267 if ( metadataFile.exists() )
268 {
269 Metadata metadata;
270
271 try
272 {
273 metadata = readMetadata( metadataFile );
274 }
275 catch ( RepositoryMetadataReadException e )
276 {
277 if ( getLogger().isDebugEnabled() )
278 {
279 getLogger().warn( e.getMessage(), e );
280 }
281 else
282 {
283 getLogger().warn( e.getMessage() );
284 }
285 return setRepository;
286 }
287
288 if ( repoMetadata.isSnapshot() && ( previousMetadata != null ) )
289 {
290 previousMetadata.put( remoteRepository, metadata );
291 }
292
293 if ( repoMetadata.getMetadata() != null )
294 {
295 setRepository = repoMetadata.getMetadata().merge( metadata );
296 }
297 else
298 {
299 repoMetadata.setMetadata( metadata );
300 setRepository = true;
301 }
302 }
303 return setRepository;
304 }
305
306
307 protected Metadata readMetadata( File mappingFile )
308 throws RepositoryMetadataReadException
309 {
310 Metadata result;
311
312 Reader reader = null;
313 try
314 {
315 reader = ReaderFactory.newXmlReader( mappingFile );
316
317 MetadataXpp3Reader mappingReader = new MetadataXpp3Reader();
318
319 result = mappingReader.read( reader, false );
320 }
321 catch ( FileNotFoundException e )
322 {
323 throw new RepositoryMetadataReadException( "Cannot read metadata from '" + mappingFile + "'", e );
324 }
325 catch ( IOException e )
326 {
327 throw new RepositoryMetadataReadException( "Cannot read metadata from '" + mappingFile + "': "
328 + e.getMessage(), e );
329 }
330 catch ( XmlPullParserException e )
331 {
332 throw new RepositoryMetadataReadException( "Cannot read metadata from '" + mappingFile + "': "
333 + e.getMessage(), e );
334 }
335 finally
336 {
337 IOUtil.close( reader );
338 }
339
340 return result;
341 }
342
343
344
345
346
347 private void fixTimestamp( File metadataFile, Metadata metadata, Metadata reference )
348 {
349 boolean changed = false;
350
351 if ( metadata != null && reference != null )
352 {
353 Versioning versioning = metadata.getVersioning();
354 Versioning versioningRef = reference.getVersioning();
355 if ( versioning != null && versioningRef != null )
356 {
357 String lastUpdated = versioning.getLastUpdated();
358 String now = versioningRef.getLastUpdated();
359 if ( lastUpdated != null && now != null && now.compareTo( lastUpdated ) < 0 )
360 {
361 getLogger().warn(
362 "The last updated timestamp in " + metadataFile + " refers to the future (now = "
363 + now + ", lastUpdated = " + lastUpdated
364 + "). Please verify that the clocks of all"
365 + " deploying machines are reasonably synchronized." );
366 versioning.setLastUpdated( now );
367 changed = true;
368 }
369 }
370 }
371
372 if ( changed )
373 {
374 getLogger().debug( "Repairing metadata in " + metadataFile );
375
376 Writer writer = null;
377 try
378 {
379 writer = WriterFactory.newXmlWriter( metadataFile );
380 new MetadataXpp3Writer().write( writer, metadata );
381 }
382 catch ( IOException e )
383 {
384 String msg = "Could not write fixed metadata to " + metadataFile + ": " + e.getMessage();
385 if ( getLogger().isDebugEnabled() )
386 {
387 getLogger().warn( msg, e );
388 }
389 else
390 {
391 getLogger().warn( msg );
392 }
393 }
394 finally
395 {
396 IOUtil.close( writer );
397 }
398 }
399 }
400
401 public void resolveAlways( RepositoryMetadata metadata, ArtifactRepository localRepository,
402 ArtifactRepository remoteRepository )
403 throws RepositoryMetadataResolutionException
404 {
405 File file;
406 try
407 {
408 file = getArtifactMetadataFromDeploymentRepository( metadata, localRepository, remoteRepository );
409 }
410 catch ( TransferFailedException e )
411 {
412 throw new RepositoryMetadataResolutionException( metadata + " could not be retrieved from repository: "
413 + remoteRepository.getId() + " due to an error: " + e.getMessage(), e );
414 }
415
416 try
417 {
418 if ( file.exists() )
419 {
420 Metadata prevMetadata = readMetadata( file );
421 metadata.setMetadata( prevMetadata );
422 }
423 }
424 catch ( RepositoryMetadataReadException e )
425 {
426 throw new RepositoryMetadataResolutionException( e.getMessage(), e );
427 }
428 }
429
430 private File getArtifactMetadataFromDeploymentRepository( ArtifactMetadata metadata,
431 ArtifactRepository localRepo,
432 ArtifactRepository remoteRepository )
433 throws TransferFailedException
434 {
435 File file =
436 new File( localRepo.getBasedir(), localRepo.pathOfLocalRepositoryMetadata( metadata, remoteRepository ) );
437
438 try
439 {
440 wagonManager.getArtifactMetadataFromDeploymentRepository( metadata, remoteRepository, file,
441 ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN );
442 }
443 catch ( ResourceDoesNotExistException e )
444 {
445 getLogger().info( metadata + " could not be found on repository: " + remoteRepository.getId()
446 + ", so will be created" );
447
448
449 if ( file.exists() )
450 {
451 file.delete();
452 }
453 }
454 finally
455 {
456 if ( metadata instanceof RepositoryMetadata )
457 {
458 updateCheckManager.touch( (RepositoryMetadata) metadata, remoteRepository, file );
459 }
460 }
461 return file;
462 }
463
464 public void deploy( ArtifactMetadata metadata, ArtifactRepository localRepository,
465 ArtifactRepository deploymentRepository )
466 throws RepositoryMetadataDeploymentException
467 {
468 File file;
469 if ( metadata instanceof RepositoryMetadata )
470 {
471 getLogger().info( "Retrieving previous metadata from " + deploymentRepository.getId() );
472 try
473 {
474 file = getArtifactMetadataFromDeploymentRepository( metadata, localRepository, deploymentRepository );
475 }
476 catch ( TransferFailedException e )
477 {
478 throw new RepositoryMetadataDeploymentException( metadata + " could not be retrieved from repository: "
479 + deploymentRepository.getId() + " due to an error: " + e.getMessage(), e );
480 }
481
482 if ( file.isFile() )
483 {
484 try
485 {
486 fixTimestamp( file, readMetadata( file ), ( (RepositoryMetadata) metadata ).getMetadata() );
487 }
488 catch ( RepositoryMetadataReadException e )
489 {
490
491 }
492 }
493 }
494 else
495 {
496
497 file =
498 new File( localRepository.getBasedir(),
499 localRepository.pathOfLocalRepositoryMetadata( metadata, deploymentRepository ) );
500 }
501
502 try
503 {
504 metadata.storeInLocalRepository( localRepository, deploymentRepository );
505 }
506 catch ( RepositoryMetadataStoreException e )
507 {
508 throw new RepositoryMetadataDeploymentException( "Error installing metadata: " + e.getMessage(), e );
509 }
510
511 try
512 {
513 wagonManager.putArtifactMetadata( file, metadata, deploymentRepository );
514 }
515 catch ( TransferFailedException e )
516 {
517 throw new RepositoryMetadataDeploymentException( "Error while deploying metadata: " + e.getMessage(), e );
518 }
519 }
520
521 public void install( ArtifactMetadata metadata, ArtifactRepository localRepository )
522 throws RepositoryMetadataInstallationException
523 {
524 try
525 {
526 metadata.storeInLocalRepository( localRepository, localRepository );
527 }
528 catch ( RepositoryMetadataStoreException e )
529 {
530 throw new RepositoryMetadataInstallationException( "Error installing metadata: " + e.getMessage(), e );
531 }
532 }
533
534 }