View Javadoc

1   package org.apache.maven.artifact.repository.metadata;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.manager.WagonManager;
23  import org.apache.maven.artifact.metadata.ArtifactMetadata;
24  import org.apache.maven.artifact.repository.ArtifactRepository;
25  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
26  import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader;
27  import org.apache.maven.wagon.ResourceDoesNotExistException;
28  import org.apache.maven.wagon.TransferFailedException;
29  import org.codehaus.plexus.logging.AbstractLogEnabled;
30  import org.codehaus.plexus.util.IOUtil;
31  import org.codehaus.plexus.util.ReaderFactory;
32  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
33  
34  import java.io.File;
35  import java.io.FileNotFoundException;
36  import java.io.IOException;
37  import java.io.Reader;
38  import java.util.Date;
39  import java.util.HashMap;
40  import java.util.HashSet;
41  import java.util.Iterator;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.Set;
45  
46  public class DefaultRepositoryMetadataManager
47      extends AbstractLogEnabled
48      implements RepositoryMetadataManager
49  {
50      // component requirement
51      private WagonManager wagonManager;
52  
53      /**
54       * @todo very primitive. Probably we can cache artifacts themselves in a central location, as well as reset the flag over time in a long running process.
55       */
56      private Set cachedMetadata = new HashSet();
57  
58      public void resolve( RepositoryMetadata metadata, List remoteRepositories, ArtifactRepository localRepository )
59          throws RepositoryMetadataResolutionException
60      {
61          boolean alreadyResolved = alreadyResolved( metadata );
62          if ( !alreadyResolved )
63          {
64              for ( Iterator i = remoteRepositories.iterator(); i.hasNext(); )
65              {
66                  ArtifactRepository repository = (ArtifactRepository) i.next();
67  
68                  ArtifactRepositoryPolicy policy =
69                      metadata.isSnapshot() ? repository.getSnapshots() : repository.getReleases();
70  
71                  if ( !policy.isEnabled() )
72                  {
73                      getLogger().debug( "Skipping disabled repository " + repository.getId() );
74                  }
75                  else if ( repository.isBlacklisted() )
76                  {
77                      getLogger().debug( "Skipping blacklisted repository " + repository.getId() );
78                  }
79                  else
80                  {
81                      File file = new File( localRepository.getBasedir(),
82                                            localRepository.pathOfLocalRepositoryMetadata( metadata, repository ) );
83  
84  
85                      boolean checkForUpdates =
86                          !file.exists() || policy.checkOutOfDate( new Date( file.lastModified() ) );
87  
88                      if ( checkForUpdates )
89                      {
90                          if ( wagonManager.isOnline() )
91                          {
92                              getLogger().info( metadata.getKey() + ": checking for updates from " + repository.getId() );
93  
94                              boolean storeMetadata = false;
95                              try
96                              {
97                                  wagonManager.getArtifactMetadata( metadata, repository, file,
98                                                                    policy.getChecksumPolicy() );
99                                  storeMetadata = true;
100                             }
101                             catch ( ResourceDoesNotExistException e )
102                             {
103                                 getLogger().debug(
104                                     metadata + " could not be found on repository: " + repository.getId() );
105 
106                                 // delete the local copy so the old details aren't used.
107                                 if ( file.exists() )
108                                 {
109                                     file.delete();
110                                 }
111                                 storeMetadata = true;
112                             }
113                             catch ( TransferFailedException e )
114                             {
115                                 getLogger().warn( metadata + " could not be retrieved from repository: " +
116                                     repository.getId() + " due to an error: " + e.getMessage() );
117                                 getLogger().debug( "Exception", e );
118 
119                                 getLogger().info( "Repository '" + repository.getId() + "' will be blacklisted" );
120                                 repository.setBlacklisted( true );
121 
122                                 // TODO: [jc; 08-Nov-2005] revisit this for 2.1
123                                 // suppressing logging to avoid logging this error twice.
124                             }
125                             if ( storeMetadata )
126                             {
127                                 // touch file so that this is not checked again until interval has passed
128                                 if ( file.exists() )
129                                 {
130                                     file.setLastModified( System.currentTimeMillis() );
131                                 }
132                                 else
133                                 {
134                                     // this ensures that files are not continuously checked when they don't exist remotely
135 
136                                     // TODO: [jdcasey] If this happens as a result of ResourceDoesNotExistException, what effect will it have on subsequent runs?
137                                     // Will the updateInterval come into play cleanly, or will this plug up the works??
138                                     try
139                                     {
140                                         metadata.storeInLocalRepository( localRepository, repository );
141                                     }
142                                     catch ( RepositoryMetadataStoreException e )
143                                     {
144                                         throw new RepositoryMetadataResolutionException(
145                                             "Unable to store local copy of metadata: " + e.getMessage(), e );
146                                     }
147                                 }
148                             }
149                         }
150                         else
151                         {
152                             getLogger().debug( "System is offline. Cannot resolve metadata:\n" +
153                                 metadata.extendedToString() + "\n\n" );
154                         }
155                     }
156                 }
157             }
158 
159             // TODO: [jdcasey] what happens here when the system is offline, or there is a TransferFailedException
160             // ...and no metadata file is written?
161             cachedMetadata.add( metadata.getKey() );
162         }
163 
164         try
165         {
166             mergeMetadata( metadata, remoteRepositories, localRepository );
167         }
168         catch ( RepositoryMetadataStoreException e )
169         {
170             throw new RepositoryMetadataResolutionException(
171                 "Unable to store local copy of metadata: " + e.getMessage(), e );
172         }
173         catch ( RepositoryMetadataReadException e )
174         {
175             throw new RepositoryMetadataResolutionException( "Unable to read local copy of metadata: " + e.getMessage(),
176                                                              e );
177         }
178     }
179 
180     private void mergeMetadata( RepositoryMetadata metadata, List remoteRepositories,
181                                 ArtifactRepository localRepository )
182         throws RepositoryMetadataStoreException, RepositoryMetadataReadException
183     {
184         // TODO: currently this is first wins, but really we should take the latest by comparing either the
185         // snapshot timestamp, or some other timestamp later encoded into the metadata.
186         // TODO: this needs to be repeated here so the merging doesn't interfere with the written metadata
187         //  - we'd be much better having a pristine input, and an ongoing metadata for merging instead
188 
189         Map previousMetadata = new HashMap();
190         ArtifactRepository selected = null;
191         for ( Iterator i = remoteRepositories.iterator(); i.hasNext(); )
192         {
193             ArtifactRepository repository = (ArtifactRepository) i.next();
194 
195             ArtifactRepositoryPolicy policy =
196                 metadata.isSnapshot() ? repository.getSnapshots() : repository.getReleases();
197 
198             if ( policy.isEnabled() && loadMetadata( metadata, repository, localRepository, previousMetadata ) )
199             {
200                 metadata.setRepository( repository );
201                 selected = repository;
202             }
203         }
204         if ( loadMetadata( metadata, localRepository, localRepository, previousMetadata ) )
205         {
206             metadata.setRepository( null );
207             selected = localRepository;
208         }
209 
210         updateSnapshotMetadata( metadata, previousMetadata, selected, localRepository );
211     }
212 
213     private void updateSnapshotMetadata( RepositoryMetadata metadata, Map previousMetadata, ArtifactRepository selected,
214                                          ArtifactRepository localRepository )
215         throws RepositoryMetadataStoreException
216     {
217         // TODO: this could be a lot nicer... should really be in the snapshot transformation?
218         if ( metadata.isSnapshot() )
219         {
220             Metadata prevMetadata = metadata.getMetadata();
221 
222             for ( Iterator i = previousMetadata.keySet().iterator(); i.hasNext(); )
223             {
224                 ArtifactRepository repository = (ArtifactRepository) i.next();
225                 Metadata m = (Metadata) previousMetadata.get( repository );
226                 if ( repository.equals( selected ) )
227                 {
228                     if ( m.getVersioning() == null )
229                     {
230                         m.setVersioning( new Versioning() );
231                     }
232 
233                     if ( m.getVersioning().getSnapshot() == null )
234                     {
235                         m.getVersioning().setSnapshot( new Snapshot() );
236                     }
237 /*
238                     if ( !m.getVersioning().getSnapshot().isLocalCopy() )
239                     {
240                         // TODO: I think this is incorrect (it results in localCopy set in a remote profile). Probably
241                         //   harmless so not removing at this point until full tests in place.
242                         m.getVersioning().getSnapshot().setLocalCopy( true );
243                         metadata.setMetadata( m );
244                         metadata.storeInLocalRepository( localRepository, repository );
245                     }
246 */
247                 }
248                 else
249                 {
250                     if ( ( m.getVersioning() != null ) && ( m.getVersioning().getSnapshot() != null ) &&
251                         m.getVersioning().getSnapshot().isLocalCopy() )
252                     {
253                         m.getVersioning().getSnapshot().setLocalCopy( false );
254                         metadata.setMetadata( m );
255                         metadata.storeInLocalRepository( localRepository, repository );
256                     }
257                 }
258             }
259 
260             metadata.setMetadata( prevMetadata );
261         }
262     }
263 
264     private boolean loadMetadata( RepositoryMetadata repoMetadata, ArtifactRepository remoteRepository,
265                                   ArtifactRepository localRepository, Map previousMetadata )
266         throws RepositoryMetadataReadException
267     {
268         boolean setRepository = false;
269 
270         File metadataFile = new File( localRepository.getBasedir(),
271                                       localRepository.pathOfLocalRepositoryMetadata( repoMetadata, remoteRepository ) );
272 
273         if ( metadataFile.exists() )
274         {
275             Metadata metadata = readMetadata( metadataFile );
276 
277             if ( repoMetadata.isSnapshot() && ( previousMetadata != null ) )
278             {
279                 previousMetadata.put( remoteRepository, metadata );
280             }
281 
282             if ( repoMetadata.getMetadata() != null )
283             {
284                 setRepository = repoMetadata.getMetadata().merge( metadata );
285             }
286             else
287             {
288                 repoMetadata.setMetadata( metadata );
289                 setRepository = true;
290             }
291         }
292         return setRepository;
293     }
294 
295     /**
296      * @todo share with DefaultPluginMappingManager.
297      */
298     protected static Metadata readMetadata( File mappingFile )
299         throws RepositoryMetadataReadException
300     {
301         Metadata result;
302 
303         Reader reader = null;
304         try
305         {
306             reader = ReaderFactory.newXmlReader( mappingFile );
307 
308             MetadataXpp3Reader mappingReader = new MetadataXpp3Reader();
309 
310             result = mappingReader.read( reader, false );
311         }
312         catch ( FileNotFoundException e )
313         {
314             throw new RepositoryMetadataReadException( "Cannot read metadata from '" + mappingFile + "'", e );
315         }
316         catch ( IOException e )
317         {
318             throw new RepositoryMetadataReadException(
319                 "Cannot read metadata from '" + mappingFile + "': " + e.getMessage(), e );
320         }
321         catch ( XmlPullParserException e )
322         {
323             throw new RepositoryMetadataReadException(
324                 "Cannot read metadata from '" + mappingFile + "': " + e.getMessage(), e );
325         }
326         finally
327         {
328             IOUtil.close( reader );
329         }
330         return result;
331     }
332 
333     public void resolveAlways( RepositoryMetadata metadata, ArtifactRepository localRepository,
334                                ArtifactRepository remoteRepository )
335         throws RepositoryMetadataResolutionException
336     {
337         if ( !wagonManager.isOnline() )
338         {
339             // metadata is required for deployment, can't be offline
340             throw new RepositoryMetadataResolutionException(
341                 "System is offline. Cannot resolve required metadata:\n" + metadata.extendedToString() );
342         }
343 
344         File file;
345         try
346         {
347             file = getArtifactMetadataFromDeploymentRepository( metadata, localRepository, remoteRepository );
348         }
349         catch ( TransferFailedException e )
350         {
351             throw new RepositoryMetadataResolutionException( metadata + " could not be retrieved from repository: " +
352                 remoteRepository.getId() + " due to an error: " + e.getMessage(), e );
353         }
354 
355         try
356         {
357             if ( file.exists() )
358             {
359                 Metadata prevMetadata = readMetadata( file );
360                 metadata.setMetadata( prevMetadata );
361             }
362         }
363         catch ( RepositoryMetadataReadException e )
364         {
365             throw new RepositoryMetadataResolutionException( e.getMessage(), e );
366         }
367     }
368 
369     private File getArtifactMetadataFromDeploymentRepository( ArtifactMetadata metadata,
370                                                               ArtifactRepository localRepository,
371                                                               ArtifactRepository remoteRepository )
372         throws TransferFailedException
373     {
374         File file = new File( localRepository.getBasedir(),
375                               localRepository.pathOfLocalRepositoryMetadata( metadata, remoteRepository ) );
376 
377         try
378         {
379             wagonManager.getArtifactMetadataFromDeploymentRepository( metadata, remoteRepository, file,
380                                                                       ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN );
381         }
382         catch ( ResourceDoesNotExistException e )
383         {
384             getLogger().info(
385                 metadata + " could not be found on repository: " + remoteRepository.getId() + ", so will be created" );
386 
387             // delete the local copy so the old details aren't used.
388             if ( file.exists() )
389             {
390                 file.delete();
391             }
392         }
393         return file;
394     }
395 
396     private boolean alreadyResolved( ArtifactMetadata metadata )
397     {
398         return cachedMetadata.contains( metadata.getKey() );
399     }
400 
401     public void deploy( ArtifactMetadata metadata, ArtifactRepository localRepository,
402                         ArtifactRepository deploymentRepository )
403         throws RepositoryMetadataDeploymentException
404     {
405         if ( !wagonManager.isOnline() )
406         {
407             // deployment shouldn't silently fail when offline
408             throw new RepositoryMetadataDeploymentException(
409                 "System is offline. Cannot deploy metadata:\n" + metadata.extendedToString() );
410         }
411 
412         File file;
413         if ( metadata instanceof RepositoryMetadata )
414         {
415             getLogger().info( "Retrieving previous metadata from " + deploymentRepository.getId() );
416             try
417             {
418                 file = getArtifactMetadataFromDeploymentRepository( metadata, localRepository, deploymentRepository );
419             }
420             catch ( TransferFailedException e )
421             {
422                 throw new RepositoryMetadataDeploymentException( metadata +
423                     " could not be retrieved from repository: " + deploymentRepository.getId() + " due to an error: " +
424                     e.getMessage(), e );
425             }
426         }
427         else
428         {
429             // It's a POM - we don't need to retrieve it first
430             file = new File( localRepository.getBasedir(),
431                              localRepository.pathOfLocalRepositoryMetadata( metadata, deploymentRepository ) );
432         }
433 
434         try
435         {
436             metadata.storeInLocalRepository( localRepository, deploymentRepository );
437         }
438         catch ( RepositoryMetadataStoreException e )
439         {
440             throw new RepositoryMetadataDeploymentException( "Error installing metadata: " + e.getMessage(), e );
441         }
442 
443         try
444         {
445             wagonManager.putArtifactMetadata( file, metadata, deploymentRepository );
446         }
447         catch ( TransferFailedException e )
448         {
449             throw new RepositoryMetadataDeploymentException( "Error while deploying metadata: " + e.getMessage(), e );
450         }
451     }
452 
453     public void install( ArtifactMetadata metadata, ArtifactRepository localRepository )
454         throws RepositoryMetadataInstallationException
455     {
456         try
457         {
458             metadata.storeInLocalRepository( localRepository, localRepository );
459         }
460         catch ( RepositoryMetadataStoreException e )
461         {
462             throw new RepositoryMetadataInstallationException( "Error installing metadata: " + e.getMessage(), e );
463         }
464     }
465 }