Coverage Report - org.apache.maven.artifact.manager.DefaultWagonManager
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultWagonManager
69%
249/360
73%
97/132
3.944
 
 1  
 package org.apache.maven.artifact.manager;
 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 java.io.File;
 23  
 import java.io.IOException;
 24  
 import java.net.MalformedURLException;
 25  
 import java.net.URL;
 26  
 import java.security.NoSuchAlgorithmException;
 27  
 import java.util.ArrayList;
 28  
 import java.util.Collection;
 29  
 import java.util.HashMap;
 30  
 import java.util.Iterator;
 31  
 import java.util.LinkedHashMap;
 32  
 import java.util.List;
 33  
 import java.util.Map;
 34  
 import java.util.Set;
 35  
 
 36  
 import org.apache.maven.artifact.Artifact;
 37  
 import org.apache.maven.artifact.metadata.ArtifactMetadata;
 38  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 39  
 import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
 40  
 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
 41  
 import org.apache.maven.artifact.repository.DefaultArtifactRepository;
 42  
 import org.apache.maven.wagon.ConnectionException;
 43  
 import org.apache.maven.wagon.ResourceDoesNotExistException;
 44  
 import org.apache.maven.wagon.TransferFailedException;
 45  
 import org.apache.maven.wagon.UnsupportedProtocolException;
 46  
 import org.apache.maven.wagon.Wagon;
 47  
 import org.apache.maven.wagon.authentication.AuthenticationException;
 48  
 import org.apache.maven.wagon.authentication.AuthenticationInfo;
 49  
 import org.apache.maven.wagon.authorization.AuthorizationException;
 50  
 import org.apache.maven.wagon.events.TransferListener;
 51  
 import org.apache.maven.wagon.observers.ChecksumObserver;
 52  
 import org.apache.maven.wagon.proxy.ProxyInfo;
 53  
 import org.apache.maven.wagon.repository.Repository;
 54  
 import org.apache.maven.wagon.repository.RepositoryPermissions;
 55  
 import org.codehaus.plexus.PlexusConstants;
 56  
 import org.codehaus.plexus.PlexusContainer;
 57  
 import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
 58  
 import org.codehaus.plexus.component.configurator.ComponentConfigurator;
 59  
 import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
 60  
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 61  
 import org.codehaus.plexus.configuration.PlexusConfiguration;
 62  
 import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
 63  
 import org.codehaus.plexus.context.Context;
 64  
 import org.codehaus.plexus.context.ContextException;
 65  
 import org.codehaus.plexus.logging.AbstractLogEnabled;
 66  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
 67  
 import org.codehaus.plexus.util.FileUtils;
 68  
 import org.codehaus.plexus.util.xml.Xpp3Dom;
 69  
 
 70  23
 public class DefaultWagonManager
 71  
     extends AbstractLogEnabled
 72  
     implements WagonManager, Contextualizable
 73  
 {
 74  
     private static final String WILDCARD = "*";
 75  
     
 76  
     private static final String EXTERNAL_WILDCARD = "external:*";
 77  
 
 78  
     private PlexusContainer container;
 79  
 
 80  
     // TODO: proxies, authentication and mirrors are via settings, and should come in via an alternate method - perhaps
 81  
     // attached to ArtifactRepository before the method is called (so AR would be composed of WR, not inherit it)
 82  23
     private Map proxies = new HashMap();
 83  
 
 84  23
     private Map authenticationInfoMap = new HashMap();
 85  
 
 86  23
     private Map serverPermissionsMap = new HashMap();
 87  
 
 88  
     //used LinkedMap to preserve the order.
 89  23
     private Map mirrors = new LinkedHashMap();
 90  
 
 91  
     /** Map( String, XmlPlexusConfiguration ) with the repository id and the wagon configuration */
 92  23
     private Map serverConfigurationMap = new HashMap();
 93  
 
 94  
     private TransferListener downloadMonitor;
 95  
 
 96  23
     private boolean online = true;
 97  
 
 98  
     private ArtifactRepositoryFactory repositoryFactory;
 99  
 
 100  23
     private boolean interactive = true;
 101  
 
 102  23
     private Map availableWagons = new HashMap();
 103  
 
 104  
     private RepositoryPermissions defaultRepositoryPermissions;
 105  
 
 106  
     // TODO: this leaks the component in the public api - it is never released back to the container
 107  
     public Wagon getWagon( Repository repository )
 108  
         throws UnsupportedProtocolException, WagonConfigurationException
 109  
     {
 110  6
         String protocol = repository.getProtocol();
 111  
 
 112  6
         if ( protocol == null )
 113  
         {
 114  1
             throw new UnsupportedProtocolException( "The repository " + repository + " does not specify a protocol" );
 115  
         }
 116  
 
 117  5
         Wagon wagon = getWagon( protocol );
 118  
 
 119  4
         configureWagon( wagon, repository.getId() );
 120  
 
 121  4
         return wagon;
 122  
     }
 123  
 
 124  
     public Wagon getWagon( String protocol )
 125  
         throws UnsupportedProtocolException
 126  
     {
 127  29
         PlexusContainer container = getWagonContainer( protocol );
 128  
 
 129  
         Wagon wagon;
 130  
         try
 131  
         {
 132  29
             wagon = (Wagon) container.lookup( Wagon.ROLE, protocol );
 133  
         }
 134  2
         catch ( ComponentLookupException e1 )
 135  
         {
 136  2
             throw new UnsupportedProtocolException(
 137  
                 "Cannot find wagon which supports the requested protocol: " + protocol, e1 );
 138  27
         }
 139  
 
 140  27
         wagon.setInteractive( interactive );
 141  
 
 142  27
         return wagon;
 143  
     }
 144  
 
 145  
     private PlexusContainer getWagonContainer( String protocol )
 146  
     {
 147  45
         PlexusContainer container = this.container;
 148  
 
 149  45
         if ( availableWagons.containsKey( protocol ) )
 150  
         {
 151  0
             container = (PlexusContainer) availableWagons.get( protocol );
 152  
         }
 153  45
         return container;
 154  
     }
 155  
 
 156  
     public void putArtifact( File source,
 157  
                              Artifact artifact,
 158  
                              ArtifactRepository deploymentRepository )
 159  
         throws TransferFailedException
 160  
     {
 161  1
         putRemoteFile( deploymentRepository, source, deploymentRepository.pathOf( artifact ), downloadMonitor );
 162  1
     }
 163  
 
 164  
     public void putArtifactMetadata( File source,
 165  
                                      ArtifactMetadata artifactMetadata,
 166  
                                      ArtifactRepository repository )
 167  
         throws TransferFailedException
 168  
     {
 169  1
         getLogger().info( "Uploading " + artifactMetadata );
 170  1
         putRemoteFile( repository, source, repository.pathOfRemoteRepositoryMetadata( artifactMetadata ), null );
 171  1
     }
 172  
 
 173  
     private void putRemoteFile( ArtifactRepository repository,
 174  
                                 File source,
 175  
                                 String remotePath,
 176  
                                 TransferListener downloadMonitor )
 177  
         throws TransferFailedException
 178  
     {
 179  2
         failIfNotOnline();
 180  
 
 181  2
         String protocol = repository.getProtocol();
 182  
 
 183  
         Wagon wagon;
 184  
         try
 185  
         {
 186  2
             wagon = getWagon( protocol );
 187  
 
 188  2
             configureWagon( wagon, repository );
 189  
         }
 190  0
         catch ( UnsupportedProtocolException e )
 191  
         {
 192  0
             throw new TransferFailedException( "Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e );
 193  2
         }
 194  
 
 195  2
         if ( downloadMonitor != null )
 196  
         {
 197  0
             wagon.addTransferListener( downloadMonitor );
 198  
         }
 199  
 
 200  2
         Map checksums = new HashMap( 2 );
 201  2
         Map sums = new HashMap( 2 );
 202  
 
 203  
         // TODO: configure these on the repository
 204  
         try
 205  
         {
 206  2
             ChecksumObserver checksumObserver = new ChecksumObserver( "MD5" );
 207  2
             wagon.addTransferListener( checksumObserver );
 208  2
             checksums.put( "md5", checksumObserver );
 209  2
             checksumObserver = new ChecksumObserver( "SHA-1" );
 210  2
             wagon.addTransferListener( checksumObserver );
 211  2
             checksums.put( "sha1", checksumObserver );
 212  
         }
 213  0
         catch ( NoSuchAlgorithmException e )
 214  
         {
 215  0
             throw new TransferFailedException( "Unable to add checksum methods: " + e.getMessage(), e );
 216  2
         }
 217  
 
 218  
         try
 219  
         {
 220  2
             Repository artifactRepository = new Repository( repository.getId(), repository.getUrl() );
 221  
 
 222  2
             if ( serverPermissionsMap.containsKey( repository.getId() ) )
 223  
             {
 224  0
                 RepositoryPermissions perms = (RepositoryPermissions) serverPermissionsMap.get( repository.getId() );
 225  
 
 226  0
                 getLogger().debug(
 227  
                     "adding permissions to wagon connection: " + perms.getFileMode() + " " + perms.getDirectoryMode() );
 228  
 
 229  0
                 artifactRepository.setPermissions( perms );
 230  0
             }
 231  
             else
 232  
             {
 233  2
                 if ( defaultRepositoryPermissions != null )
 234  
                 {
 235  0
                     artifactRepository.setPermissions( defaultRepositoryPermissions );
 236  
                 }
 237  
                 else
 238  
                 {
 239  2
                     getLogger().debug( "not adding permissions to wagon connection" );
 240  
                 }
 241  
             }
 242  
 
 243  2
             wagon.connect( artifactRepository, getAuthenticationInfo( repository.getId() ), getProxy(protocol));
 244  
 
 245  2
             wagon.put( source, remotePath );
 246  
 
 247  2
             wagon.removeTransferListener( downloadMonitor );
 248  
 
 249  
             // Pre-store the checksums as any future puts will overwrite them
 250  2
             for ( Iterator i = checksums.keySet().iterator(); i.hasNext(); )
 251  
             {
 252  4
                 String extension = (String) i.next();
 253  4
                 ChecksumObserver observer = (ChecksumObserver) checksums.get( extension );
 254  4
                 sums.put( extension, observer.getActualChecksum() );
 255  4
             }
 256  
 
 257  
             // We do this in here so we can checksum the artifact metadata too, otherwise it could be metadata itself
 258  2
             for ( Iterator i = checksums.keySet().iterator(); i.hasNext(); )
 259  
             {
 260  4
                 String extension = (String) i.next();
 261  
 
 262  
                 // TODO: shouldn't need a file intermediatary - improve wagon to take a stream
 263  4
                 File temp = File.createTempFile( "maven-artifact", null );
 264  4
                 temp.deleteOnExit();
 265  4
                 FileUtils.fileWrite( temp.getAbsolutePath(), "UTF-8", (String) sums.get( extension ) );
 266  
 
 267  4
                 wagon.put( temp, remotePath + "." + extension );
 268  4
             }
 269  
         }
 270  0
         catch ( ConnectionException e )
 271  
         {
 272  0
             throw new TransferFailedException( "Connection failed: " + e.getMessage(), e );
 273  
         }
 274  0
         catch ( AuthenticationException e )
 275  
         {
 276  0
             throw new TransferFailedException( "Authentication failed: " + e.getMessage(), e );
 277  
         }
 278  0
         catch ( AuthorizationException e )
 279  
         {
 280  0
             throw new TransferFailedException( "Authorization failed: " + e.getMessage(), e );
 281  
         }
 282  0
         catch ( ResourceDoesNotExistException e )
 283  
         {
 284  0
             throw new TransferFailedException( "Resource to deploy not found: " + e.getMessage(), e );
 285  
         }
 286  0
         catch ( IOException e )
 287  
         {
 288  0
             throw new TransferFailedException( "Error creating temporary file for deployment: " + e.getMessage(), e );
 289  
         }
 290  
         finally
 291  
         {
 292  2
             disconnectWagon( wagon );
 293  
 
 294  2
             releaseWagon( protocol, wagon );
 295  2
         }
 296  2
     }
 297  
 
 298  
     public void getArtifact( Artifact artifact,
 299  
                              List remoteRepositories )
 300  
         throws TransferFailedException, ResourceDoesNotExistException
 301  
     {
 302  
         // TODO [BP]: The exception handling here needs some work
 303  6
         boolean successful = false;
 304  6
         for ( Iterator iter = remoteRepositories.iterator(); iter.hasNext() && !successful; )
 305  
         {
 306  6
             ArtifactRepository repository = (ArtifactRepository) iter.next();
 307  
 
 308  
             try
 309  
             {
 310  6
                 getArtifact( artifact, repository );
 311  
 
 312  4
                 successful = artifact.isResolved();
 313  
             }
 314  2
             catch ( ResourceDoesNotExistException e )
 315  
             {
 316  
                 // This one we will eat when looking through remote repositories
 317  
                 // because we want to cycle through them all before squawking.
 318  
 
 319  2
                 getLogger().info( "Unable to find resource '" + artifact.getId() + "' in repository " +
 320  
                     repository.getId() + " (" + repository.getUrl() + ")" );
 321  
             }
 322  0
             catch ( TransferFailedException e )
 323  
             {
 324  0
                 getLogger().warn( "Unable to get resource '" + artifact.getId() + "' from repository " +
 325  
                     repository.getId() + " (" + repository.getUrl() + "): " + e.getMessage() );
 326  6
             }
 327  6
         }
 328  
 
 329  
         // if it already exists locally we were just trying to force it - ignore the update
 330  6
         if ( !successful && !artifact.getFile().exists() )
 331  
         {
 332  2
             throw new ResourceDoesNotExistException( "Unable to download the artifact from any repository" );
 333  
         }
 334  4
     }
 335  
 
 336  
     public void getArtifact( Artifact artifact,
 337  
                              ArtifactRepository repository )
 338  
         throws TransferFailedException, ResourceDoesNotExistException
 339  
     {
 340  13
         String remotePath = repository.pathOf( artifact );
 341  
 
 342  13
         ArtifactRepositoryPolicy policy = artifact.isSnapshot() ? repository.getSnapshots() : repository.getReleases();
 343  
 
 344  13
         if ( !policy.isEnabled() )
 345  
         {
 346  0
             getLogger().debug( "Skipping disabled repository " + repository.getId() );
 347  
         }
 348  13
         else if ( repository.isBlacklisted() )
 349  
         {
 350  0
             getLogger().debug( "Skipping blacklisted repository " + repository.getId() );
 351  
         }
 352  
         else
 353  
         {
 354  13
             getLogger().debug( "Trying repository " + repository.getId() );
 355  13
             getRemoteFile( getMirrorRepository( repository ), artifact.getFile(), remotePath, downloadMonitor,
 356  
                                    policy.getChecksumPolicy(), false );
 357  9
             getLogger().debug( "  Artifact resolved" );
 358  
 
 359  9
             artifact.setResolved( true );
 360  
         }
 361  9
     }
 362  
 
 363  
     public void getArtifactMetadata( ArtifactMetadata metadata,
 364  
                                      ArtifactRepository repository,
 365  
                                      File destination,
 366  
                                      String checksumPolicy )
 367  
         throws TransferFailedException, ResourceDoesNotExistException
 368  
     {
 369  0
         String remotePath = repository.pathOfRemoteRepositoryMetadata( metadata );
 370  
 
 371  0
         getRemoteFile( getMirrorRepository( repository ), destination, remotePath, null, checksumPolicy, true );
 372  0
     }
 373  
 
 374  
     public void getArtifactMetadataFromDeploymentRepository( ArtifactMetadata metadata, ArtifactRepository repository,
 375  
                                                              File destination, String checksumPolicy )
 376  
         throws TransferFailedException, ResourceDoesNotExistException
 377  
     {
 378  1
         String remotePath = repository.pathOfRemoteRepositoryMetadata( metadata );
 379  
 
 380  1
         getRemoteFile( repository, destination, remotePath, null, checksumPolicy, true );
 381  1
     }
 382  
 
 383  
     private void getRemoteFile( ArtifactRepository repository,
 384  
                                 File destination,
 385  
                                 String remotePath,
 386  
                                 TransferListener downloadMonitor,
 387  
                                 String checksumPolicy,
 388  
                                 boolean force )
 389  
         throws TransferFailedException, ResourceDoesNotExistException
 390  
     {
 391  
         // TODO: better excetpions - transfer failed is not enough?
 392  
 
 393  14
         failIfNotOnline();
 394  
 
 395  14
         String protocol = repository.getProtocol();
 396  
         Wagon wagon;
 397  
         try
 398  
         {
 399  14
             wagon = getWagon( protocol );
 400  
 
 401  14
             configureWagon( wagon, repository );
 402  
         }
 403  0
         catch ( UnsupportedProtocolException e )
 404  
         {
 405  0
             throw new TransferFailedException( "Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e );
 406  14
         }
 407  
 
 408  14
         if ( downloadMonitor != null )
 409  
         {
 410  0
             wagon.addTransferListener( downloadMonitor );
 411  
         }
 412  
 
 413  
         // TODO: configure on repository
 414  
         ChecksumObserver md5ChecksumObserver;
 415  
         ChecksumObserver sha1ChecksumObserver;
 416  
         
 417  14
         List checksumObservers = new ArrayList( 2 );
 418  
         try
 419  
         {
 420  14
             md5ChecksumObserver = new ChecksumObserver( "MD5" );
 421  14
             wagon.addTransferListener( md5ChecksumObserver );
 422  14
             checksumObservers.add( md5ChecksumObserver );
 423  
 
 424  14
             sha1ChecksumObserver = new ChecksumObserver( "SHA-1" );
 425  14
             wagon.addTransferListener( sha1ChecksumObserver );
 426  14
             checksumObservers.add( sha1ChecksumObserver );
 427  
         }
 428  0
         catch ( NoSuchAlgorithmException e )
 429  
         {
 430  0
             throw new TransferFailedException( "Unable to add checksum methods: " + e.getMessage(), e );
 431  14
         }
 432  
 
 433  14
         File temp = new File( destination + ".tmp" );
 434  14
         temp.deleteOnExit();
 435  
 
 436  14
         boolean downloaded = false;
 437  
 
 438  
         try
 439  
         {
 440  14
             wagon.connect( new Repository( repository.getId(), repository.getUrl() ),
 441  
                            getAuthenticationInfo( repository.getId() ),getProxy(protocol));
 442  
 
 443  14
             boolean firstRun = true;
 444  14
             boolean retry = true;
 445  
 
 446  
             // this will run at most twice. The first time, the firstRun flag is turned off, and if the retry flag
 447  
             // is set on the first run, it will be turned off and not re-set on the second try. This is because the
 448  
             // only way the retry flag can be set is if ( firstRun == true ).
 449  27
             while ( firstRun || retry )
 450  
             {
 451  
                 // reset the retry flag.
 452  17
                 retry = false;
 453  
 
 454  
                 // This should take care of creating destination directory now on
 455  17
                 if ( destination.exists() && !force )
 456  
                 {
 457  
                     try
 458  
                     {
 459  0
                         downloaded = wagon.getIfNewer( remotePath, temp, destination.lastModified() );
 460  0
                         if ( !downloaded )
 461  
                         {
 462  
                             // prevent additional checks of this artifact until it expires again
 463  0
                             destination.setLastModified( System.currentTimeMillis() );
 464  
                         }
 465  
                     }
 466  0
                     catch ( UnsupportedOperationException e )
 467  
                     {
 468  
                         // older wagons throw this. Just get() instead
 469  0
                         wagon.get( remotePath, temp );
 470  0
                         downloaded = true;
 471  0
                     }
 472  
                 }
 473  
                 else
 474  
                 {
 475  17
                     wagon.get( remotePath, temp );
 476  15
                     downloaded = true;
 477  
                 }
 478  
 
 479  15
                 if ( downloaded )
 480  
                 {
 481  
                     // keep the checksum files from showing up on the download monitor...
 482  15
                     if ( downloadMonitor != null )
 483  
                     {
 484  0
                         wagon.removeTransferListener( downloadMonitor );
 485  
                     }
 486  
 
 487  
                     // try to verify the SHA-1 checksum for this file.
 488  
                     try
 489  
                     {
 490  15
                         verifyChecksum( sha1ChecksumObserver, destination, temp, remotePath, ".sha1", wagon, checksumObservers );
 491  
                     }
 492  2
                     catch ( ChecksumFailedException e )
 493  
                     {
 494  
                         // if we catch a ChecksumFailedException, it means the transfer/read succeeded, but the checksum
 495  
                         // doesn't match. This could be a problem with the server (ibiblio HTTP-200 error page), so we'll
 496  
                         // try this up to two times. On the second try, we'll handle it as a bona-fide error, based on the
 497  
                         // repository's checksum checking policy.
 498  2
                         if ( firstRun )
 499  
                         {
 500  1
                             getLogger().warn( "*** CHECKSUM FAILED - " + e.getMessage() + " - RETRYING" );
 501  1
                             retry = true;
 502  
                         }
 503  
                         else
 504  
                         {
 505  1
                             handleChecksumFailure( checksumPolicy, e.getMessage(), e.getCause() );
 506  
                         }
 507  
                     }
 508  10
                     catch ( ResourceDoesNotExistException sha1TryException )
 509  
                     {
 510  10
                         getLogger().debug( "SHA1 not found, trying MD5", sha1TryException );
 511  
 
 512  
                         // if this IS NOT a ChecksumFailedException, it was a problem with transfer/read of the checksum
 513  
                         // file...we'll try again with the MD5 checksum.
 514  
                         try
 515  
                         {
 516  10
                             verifyChecksum( md5ChecksumObserver, destination, temp, remotePath, ".md5", wagon, checksumObservers );
 517  
                         }
 518  4
                         catch ( ChecksumFailedException e )
 519  
                         {
 520  
                             // if we also fail to verify based on the MD5 checksum, and the checksum transfer/read
 521  
                             // succeeded, then we need to determine whether to retry or handle it as a failure.
 522  4
                             if ( firstRun )
 523  
                             {
 524  2
                                 retry = true;
 525  
                             }
 526  
                             else
 527  
                             {
 528  2
                                 handleChecksumFailure( checksumPolicy, e.getMessage(), e.getCause() );
 529  
                             }
 530  
                         }
 531  4
                         catch ( ResourceDoesNotExistException md5TryException )
 532  
                         {
 533  
                             // this was a failed transfer, and we don't want to retry.
 534  4
                             handleChecksumFailure( checksumPolicy, "Error retrieving checksum file for " + remotePath,
 535  
                                                    md5TryException );
 536  5
                         }
 537  4
                     }
 538  
 
 539  
                     // reinstate the download monitor...
 540  13
                     if ( downloadMonitor != null )
 541  
                     {
 542  0
                         wagon.addTransferListener( downloadMonitor );
 543  
                     }
 544  
                 }
 545  
 
 546  
                 // unset the firstRun flag, so we don't get caught in an infinite loop...
 547  13
                 firstRun = false;
 548  
             }
 549  
         }
 550  0
         catch ( ConnectionException e )
 551  
         {
 552  0
             throw new TransferFailedException( "Connection failed: " + e.getMessage(), e );
 553  
         }
 554  0
         catch ( AuthenticationException e )
 555  
         {
 556  0
             throw new TransferFailedException( "Authentication failed: " + e.getMessage(), e );
 557  
         }
 558  0
         catch ( AuthorizationException e )
 559  
         {
 560  0
             throw new TransferFailedException( "Authorization failed: " + e.getMessage(), e );
 561  
         }
 562  
         finally
 563  
         {
 564  14
             disconnectWagon( wagon );
 565  
 
 566  14
             releaseWagon( protocol, wagon );
 567  10
         }
 568  
 
 569  10
         if ( downloaded )
 570  
         {
 571  10
             if ( !temp.exists() )
 572  
             {
 573  0
                 throw new ResourceDoesNotExistException( "Downloaded file does not exist: " + temp );
 574  
             }
 575  
 
 576  
             // The temporary file is named destination + ".tmp" and is done this way to ensure
 577  
             // that the temporary file is in the same file system as the destination because the
 578  
             // File.renameTo operation doesn't really work across file systems.
 579  
             // So we will attempt to do a File.renameTo for efficiency and atomicity, if this fails
 580  
             // then we will use a brute force copy and delete the temporary file.
 581  
 
 582  10
             if ( !temp.renameTo( destination ) )
 583  
             {
 584  
                 try
 585  
                 {
 586  0
                     FileUtils.copyFile( temp, destination );
 587  
 
 588  0
                     temp.delete();
 589  
                 }
 590  0
                 catch ( IOException e )
 591  
                 {
 592  0
                     throw new TransferFailedException(
 593  
                         "Error copying temporary file to the final destination: " + e.getMessage(), e );
 594  0
                 }
 595  
             }
 596  
         }
 597  10
     }
 598  
 
 599  
     public ArtifactRepository getMirrorRepository( ArtifactRepository repository )
 600  
     {
 601  27
         ArtifactRepository mirror = getMirror( repository );
 602  27
         if ( mirror != null )
 603  
         {
 604  13
             String id = mirror.getId();
 605  13
             if ( id == null )
 606  
             {
 607  
                 // TODO: this should be illegal in settings.xml
 608  0
                 id = repository.getId();
 609  
             }
 610  
 
 611  13
             repository = repositoryFactory.createArtifactRepository( id, mirror.getUrl(),
 612  
                                                                      repository.getLayout(), repository.getSnapshots(),
 613  
                                                                      repository.getReleases() );
 614  
         }
 615  27
         return repository;
 616  
     }
 617  
 
 618  
     private void failIfNotOnline()
 619  
         throws TransferFailedException
 620  
     {
 621  16
         if ( !isOnline() )
 622  
         {
 623  0
             throw new TransferFailedException( "System is offline." );
 624  
         }
 625  16
     }
 626  
 
 627  
     private void handleChecksumFailure( String checksumPolicy,
 628  
                                         String message,
 629  
                                         Throwable cause )
 630  
         throws ChecksumFailedException
 631  
     {
 632  7
         if ( ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL.equals( checksumPolicy ) )
 633  
         {
 634  2
             throw new ChecksumFailedException( message, cause );
 635  
         }
 636  5
         else if ( !ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals( checksumPolicy ) )
 637  
         {
 638  
             // warn if it is set to anything other than ignore
 639  4
             getLogger().warn( "*** CHECKSUM FAILED - " + message + " - IGNORING" );
 640  
         }
 641  
         // otherwise it is ignore
 642  5
     }
 643  
 
 644  
     private void verifyChecksum( ChecksumObserver checksumObserver,
 645  
                                  File destination,
 646  
                                  File tempDestination,
 647  
                                  String remotePath,
 648  
                                  String checksumFileExtension,
 649  
                                  Wagon wagon, 
 650  
                                  List checksumObservers )
 651  
         throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException
 652  
     {
 653  
         // FIXME: We need to be able to disable/retrieve/manipulate existing checksum observers on wagon instances.
 654  
         // Otherwise, each time a checksum GET throws ResourceNotFoundException, all other checksum data in other
 655  
         // observers for the main artifact have their actual checksums destroyed.
 656  25
         for ( Iterator it = checksumObservers.iterator(); it.hasNext(); )
 657  
         {
 658  50
             ChecksumObserver observer = (ChecksumObserver) it.next();
 659  50
             wagon.removeTransferListener( observer );
 660  50
         }
 661  
         
 662  
         try
 663  
         {
 664  
             // grab it first, because it's about to change...
 665  25
             String actualChecksum = checksumObserver.getActualChecksum();
 666  
 
 667  25
             File tempChecksumFile = new File( tempDestination + checksumFileExtension + ".tmp" );
 668  25
             tempChecksumFile.deleteOnExit();
 669  25
             wagon.get( remotePath + checksumFileExtension, tempChecksumFile );
 670  
 
 671  11
             String expectedChecksum = FileUtils.fileRead( tempChecksumFile, "UTF-8" );
 672  
 
 673  
             // remove whitespaces at the end
 674  11
             expectedChecksum = expectedChecksum.trim();
 675  
 
 676  
             // check for 'ALGO (name) = CHECKSUM' like used by openssl
 677  11
             if ( expectedChecksum.regionMatches( true, 0, "MD", 0, 2 )
 678  
                 || expectedChecksum.regionMatches( true, 0, "SHA", 0, 3 ) )
 679  
             {
 680  0
                 int lastSpacePos = expectedChecksum.lastIndexOf( ' ' );
 681  0
                 expectedChecksum = expectedChecksum.substring( lastSpacePos + 1 );
 682  0
             }
 683  
             else
 684  
             {
 685  
                 // remove everything after the first space (if available)
 686  11
                 int spacePos = expectedChecksum.indexOf( ' ' );
 687  
 
 688  11
                 if ( spacePos != -1 )
 689  
                 {
 690  0
                     expectedChecksum = expectedChecksum.substring( 0, spacePos );
 691  
                 }
 692  
             }
 693  11
             if ( expectedChecksum.equalsIgnoreCase( actualChecksum ) )
 694  
             {
 695  5
                 File checksumFile = new File( destination + checksumFileExtension );
 696  5
                 if ( checksumFile.exists() )
 697  
                 {
 698  4
                     checksumFile.delete();
 699  
                 }
 700  5
                 FileUtils.copyFile( tempChecksumFile, checksumFile );
 701  5
             }
 702  
             else
 703  
             {
 704  6
                 throw new ChecksumFailedException( "Checksum failed on download: local = '" + actualChecksum +
 705  
                     "'; remote = '" + expectedChecksum + "'" );
 706  
             }
 707  
         }
 708  0
         catch ( IOException e )
 709  
         {
 710  0
             throw new ChecksumFailedException( "Invalid checksum file", e );
 711  
         }
 712  
         finally
 713  
         {
 714  25
             for ( Iterator it = checksumObservers.iterator(); it.hasNext(); )
 715  
             {
 716  50
                 ChecksumObserver observer = (ChecksumObserver) it.next();
 717  50
                 wagon.addTransferListener( observer );
 718  50
             }
 719  5
         }
 720  5
     }
 721  
 
 722  
     
 723  
     private void disconnectWagon( Wagon wagon )
 724  
     {
 725  
         try
 726  
         {
 727  16
             wagon.disconnect();
 728  
         }
 729  0
         catch ( ConnectionException e )
 730  
         {
 731  0
             getLogger().error( "Problem disconnecting from wagon - ignoring: " + e.getMessage() );
 732  16
         }
 733  16
     }
 734  
 
 735  
     private void releaseWagon( String protocol,
 736  
                                Wagon wagon )
 737  
     {
 738  16
         PlexusContainer container = getWagonContainer( protocol );
 739  
         try
 740  
         {
 741  16
             container.release( wagon );
 742  
         }
 743  0
         catch ( ComponentLifecycleException e )
 744  
         {
 745  0
             getLogger().error( "Problem releasing wagon - ignoring: " + e.getMessage() );
 746  16
         }
 747  16
     }
 748  
 
 749  
     public ProxyInfo getProxy( String protocol )
 750  
     {
 751  16
         return (ProxyInfo) proxies.get( protocol );
 752  
     }
 753  
 
 754  
     public AuthenticationInfo getAuthenticationInfo( String id )
 755  
     {
 756  16
         return (AuthenticationInfo) authenticationInfoMap.get( id );
 757  
     }
 758  
 
 759  
     /**
 760  
      * This method finds a matching mirror for the selected repository. If there is an exact match, this will be used.
 761  
      * If there is no exact match, then the list of mirrors is examined to see if a pattern applies.
 762  
      * 
 763  
      * @param originalRepository See if there is a mirror for this repository.
 764  
      * @return the selected mirror or null if none are found.
 765  
      */
 766  
     public ArtifactRepository getMirror( ArtifactRepository originalRepository )
 767  
     {
 768  27
         ArtifactRepository selectedMirror = (ArtifactRepository) mirrors.get( originalRepository.getId() );
 769  27
         if ( null == selectedMirror )
 770  
         {
 771  
             // Process the patterns in order. First one that matches wins.
 772  19
             Set keySet = mirrors.keySet();
 773  19
             if ( keySet != null )
 774  
             {
 775  19
                 Iterator iter = keySet.iterator();
 776  37
                 while ( iter.hasNext() )
 777  
                 {
 778  23
                     String pattern = (String) iter.next();
 779  23
                     if ( matchPattern( originalRepository, pattern ) )
 780  
                     {
 781  5
                         selectedMirror = (ArtifactRepository) mirrors.get( pattern );
 782  5
                         break;
 783  
                     }
 784  18
                 }
 785  
             }
 786  
 
 787  
         }
 788  27
         return selectedMirror;
 789  
     }
 790  
 
 791  
     /**
 792  
      * This method checks if the pattern matches the originalRepository. 
 793  
      * Valid patterns: 
 794  
      * * = everything
 795  
      * external:* = everything not on the localhost and not file based.
 796  
      * repo,repo1 = repo or repo1
 797  
      * *,!repo1 = everything except repo1
 798  
      * 
 799  
      * @param originalRepository to compare for a match.
 800  
      * @param pattern used for match. Currently only '*' is supported.
 801  
      * @return true if the repository is a match to this pattern.
 802  
      */
 803  
     public boolean matchPattern( ArtifactRepository originalRepository, String pattern )
 804  
     {
 805  55
         boolean result = false;
 806  55
         String originalId = originalRepository.getId();
 807  
 
 808  
         // simple checks first to short circuit processing below.
 809  55
         if ( WILDCARD.equals( pattern ) || pattern.equals( originalId ) )
 810  
         {
 811  7
             result = true;
 812  
         }
 813  
         else
 814  
         {
 815  
             // process the list
 816  48
             String[] repos = pattern.split( "," );
 817  106
             for ( int i = 0; i < repos.length; i++ )
 818  
             {
 819  72
                 String repo = repos[i];
 820  
 
 821  
                 // see if this is a negative match
 822  72
                 if ( repo.length() > 1 && repo.startsWith( "!" ) )
 823  
                 {
 824  13
                     if ( originalId.equals( repo.substring( 1 ) ) )
 825  
                     {
 826  
                         // explicitly exclude. Set result and stop processing.
 827  5
                         result = false;
 828  5
                         break;
 829  
                     }
 830  
                 }
 831  
                 // check for exact match
 832  59
                 else if ( originalId.equals( repo ) )
 833  
                 {
 834  9
                     result = true;
 835  9
                     break;
 836  
                 }
 837  
                 // check for external:*
 838  50
                 else if ( EXTERNAL_WILDCARD.equals( repo ) && isExternalRepo( originalRepository ) )
 839  
                 {
 840  1
                     result = true;
 841  
                     // don't stop processing in case a future segment explicitly excludes this repo
 842  
                 }
 843  49
                 else if ( WILDCARD.equals( repo ) )
 844  
                 {
 845  8
                     result = true;
 846  
                     // don't stop processing in case a future segment explicitly excludes this repo
 847  
                 }
 848  
             }
 849  
         }
 850  55
         return result;
 851  
     }
 852  
 
 853  
     /**
 854  
      * Checks the URL to see if this repository refers to an external repository
 855  
      * 
 856  
      * @param originalRepository
 857  
      * @return true if external.
 858  
      */
 859  
     public boolean isExternalRepo( ArtifactRepository originalRepository )
 860  
     {
 861  
         try
 862  
         {
 863  20
             URL url = new URL( originalRepository.getUrl() );
 864  18
             return !( url.getHost().equals( "localhost" ) || url.getHost().equals( "127.0.0.1" ) || url.getProtocol().equals(
 865  
                                                                                                                               "file" ) );
 866  
         }
 867  2
         catch ( MalformedURLException e )
 868  
         {
 869  
             // bad url just skip it here. It should have been validated already, but the wagon lookup will deal with it
 870  2
             return false;
 871  
         }
 872  
     }
 873  
     
 874  
     /**
 875  
      * Set the proxy used for a particular protocol.
 876  
      * 
 877  
      * @param protocol the protocol (required)
 878  
      * @param host the proxy host name (required)
 879  
      * @param port the proxy port (required)
 880  
      * @param username the username for the proxy, or null if there is none
 881  
      * @param password the password for the proxy, or null if there is none
 882  
      * @param nonProxyHosts the set of hosts not to use the proxy for. Follows Java system property format:
 883  
      *            <code>*.foo.com|localhost</code>.
 884  
      * @todo [BP] would be nice to configure this via plexus in some way
 885  
      */
 886  
     public void addProxy( String protocol,
 887  
                           String host,
 888  
                           int port,
 889  
                           String username,
 890  
                           String password,
 891  
                           String nonProxyHosts )
 892  
     {
 893  0
         ProxyInfo proxyInfo = new ProxyInfo();
 894  0
         proxyInfo.setHost( host );
 895  0
         proxyInfo.setType( protocol );
 896  0
         proxyInfo.setPort( port );
 897  0
         proxyInfo.setNonProxyHosts( nonProxyHosts );
 898  0
         proxyInfo.setUserName( username );
 899  0
         proxyInfo.setPassword( password );
 900  
 
 901  0
         proxies.put( protocol, proxyInfo );
 902  0
     }
 903  
 
 904  
     public void contextualize( Context context )
 905  
         throws ContextException
 906  
     {
 907  20
         container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
 908  20
     }
 909  
 
 910  
     /** @todo I'd rather not be setting this explicitly. */
 911  
     public void setDownloadMonitor( TransferListener downloadMonitor )
 912  
     {
 913  0
         this.downloadMonitor = downloadMonitor;
 914  0
     }
 915  
 
 916  
     public void addAuthenticationInfo( String repositoryId,
 917  
                                        String username,
 918  
                                        String password,
 919  
                                        String privateKey,
 920  
                                        String passphrase )
 921  
     {
 922  0
         AuthenticationInfo authInfo = new AuthenticationInfo();
 923  
 
 924  0
         authInfo.setUserName( username );
 925  
 
 926  0
         authInfo.setPassword( password );
 927  
 
 928  0
         authInfo.setPrivateKey( privateKey );
 929  
 
 930  0
         authInfo.setPassphrase( passphrase );
 931  
 
 932  0
         authenticationInfoMap.put( repositoryId, authInfo );
 933  0
     }
 934  
 
 935  
     public void addPermissionInfo( String repositoryId,
 936  
                                    String filePermissions,
 937  
                                    String directoryPermissions )
 938  
     {
 939  
 
 940  0
         RepositoryPermissions permissions = new RepositoryPermissions();
 941  0
         boolean addPermissions = false;
 942  
 
 943  0
         if ( filePermissions != null )
 944  
         {
 945  0
             permissions.setFileMode( filePermissions );
 946  0
             addPermissions = true;
 947  
         }
 948  
 
 949  0
         if ( directoryPermissions != null )
 950  
         {
 951  0
             permissions.setDirectoryMode( directoryPermissions );
 952  0
             addPermissions = true;
 953  
         }
 954  
 
 955  0
         if ( addPermissions )
 956  
         {
 957  0
             serverPermissionsMap.put( repositoryId, permissions );
 958  
         }
 959  0
     }
 960  
 
 961  
     public void addMirror( String id,
 962  
                            String mirrorOf,
 963  
                            String url )
 964  
     {
 965  13
         ArtifactRepository mirror = new DefaultArtifactRepository( id, url, null );
 966  
 
 967  
         //to preserve first wins, don't add repeated mirrors.
 968  13
         if (!mirrors.containsKey( mirrorOf ))
 969  
         {
 970  12
             mirrors.put( mirrorOf, mirror );
 971  
         }
 972  13
     }
 973  
 
 974  
     public void setOnline( boolean online )
 975  
     {
 976  0
         this.online = online;
 977  0
     }
 978  
 
 979  
     public boolean isOnline()
 980  
     {
 981  24
         return online;
 982  
     }
 983  
 
 984  
     public void setInteractive( boolean interactive )
 985  
     {
 986  0
         this.interactive = interactive;
 987  0
     }
 988  
 
 989  
     public void registerWagons( Collection wagons,
 990  
                                 PlexusContainer extensionContainer )
 991  
     {
 992  0
         for ( Iterator i = wagons.iterator(); i.hasNext(); )
 993  
         {
 994  0
             availableWagons.put( i.next(), extensionContainer );
 995  
         }
 996  0
     }
 997  
 
 998  
     /**
 999  
      * Applies the server configuration to the wagon
 1000  
      *
 1001  
      * @param wagon      the wagon to configure
 1002  
      * @param repository the repository that has the configuration
 1003  
      * @throws WagonConfigurationException wraps any error given during configuration of the wagon instance
 1004  
      */
 1005  
     private void configureWagon( Wagon wagon,
 1006  
                                  ArtifactRepository repository )
 1007  
         throws WagonConfigurationException
 1008  
     {
 1009  16
         configureWagon( wagon, repository.getId() );
 1010  16
     }
 1011  
 
 1012  
     private void configureWagon( Wagon wagon,
 1013  
                                  String repositoryId )
 1014  
         throws WagonConfigurationException
 1015  
     {
 1016  20
         if ( serverConfigurationMap.containsKey( repositoryId ) )
 1017  
         {
 1018  4
             ComponentConfigurator componentConfigurator = null;
 1019  
             try
 1020  
             {
 1021  4
                 componentConfigurator = (ComponentConfigurator) container.lookup( ComponentConfigurator.ROLE );
 1022  4
                 componentConfigurator.configureComponent( wagon, (PlexusConfiguration) serverConfigurationMap
 1023  
                     .get( repositoryId ), container.getContainerRealm() );
 1024  
             }
 1025  0
             catch ( final ComponentLookupException e )
 1026  
             {
 1027  0
                 throw new WagonConfigurationException( repositoryId,
 1028  
                                                        "Unable to lookup wagon configurator. Wagon configuration cannot be applied.",
 1029  
                                                        e );
 1030  
             }
 1031  0
             catch ( ComponentConfigurationException e )
 1032  
             {
 1033  0
                 throw new WagonConfigurationException( repositoryId, "Unable to apply wagon configuration.", e );
 1034  
             }
 1035  
             finally
 1036  
             {
 1037  4
                 if ( componentConfigurator != null )
 1038  
                 {
 1039  
                     try
 1040  
                     {
 1041  4
                         container.release( componentConfigurator );
 1042  
                     }
 1043  0
                     catch ( ComponentLifecycleException e )
 1044  
                     {
 1045  0
                         getLogger().error( "Problem releasing configurator - ignoring: " + e.getMessage() );
 1046  4
                     }
 1047  
                 }
 1048  
 
 1049  
             }
 1050  
         }
 1051  20
     }
 1052  
 
 1053  
     public void addConfiguration( String repositoryId,
 1054  
                                   Xpp3Dom configuration )
 1055  
     {
 1056  5
         if ( repositoryId == null || configuration == null )
 1057  
         {
 1058  0
             throw new IllegalArgumentException( "arguments can't be null" );
 1059  
         }
 1060  
 
 1061  5
         final XmlPlexusConfiguration xmlConf = new XmlPlexusConfiguration( configuration );
 1062  
 
 1063  5
         serverConfigurationMap.put( repositoryId, xmlConf );
 1064  5
     }
 1065  
 
 1066  
     public void setDefaultRepositoryPermissions( RepositoryPermissions defaultRepositoryPermissions )
 1067  
     {
 1068  0
         this.defaultRepositoryPermissions = defaultRepositoryPermissions;
 1069  0
     }
 1070  
 }