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