View Javadoc

1   package org.apache.maven.repository.legacy;
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.security.NoSuchAlgorithmException;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.artifact.metadata.ArtifactMetadata;
32  import org.apache.maven.artifact.repository.ArtifactRepository;
33  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
34  import org.apache.maven.wagon.ConnectionException;
35  import org.apache.maven.wagon.ResourceDoesNotExistException;
36  import org.apache.maven.wagon.TransferFailedException;
37  import org.apache.maven.wagon.UnsupportedProtocolException;
38  import org.apache.maven.wagon.Wagon;
39  import org.apache.maven.wagon.authentication.AuthenticationException;
40  import org.apache.maven.wagon.authentication.AuthenticationInfo;
41  import org.apache.maven.wagon.authorization.AuthorizationException;
42  import org.apache.maven.wagon.events.TransferListener;
43  import org.apache.maven.wagon.observers.ChecksumObserver;
44  import org.apache.maven.wagon.proxy.ProxyInfo;
45  import org.apache.maven.wagon.repository.Repository;
46  import org.codehaus.plexus.PlexusContainer;
47  import org.codehaus.plexus.component.annotations.Component;
48  import org.codehaus.plexus.component.annotations.Requirement;
49  import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
50  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
51  import org.codehaus.plexus.logging.Logger;
52  import org.codehaus.plexus.util.FileUtils;
53  
54  //TODO: remove the update check manager
55  //TODO: separate into retriever and publisher
56  //TODO: remove hardcoding of checksum logic
57  @Component( role = WagonManager.class )
58  public class DefaultWagonManager
59      implements WagonManager
60  {
61      private static final String[] CHECKSUM_IDS = { "md5", "sha1" };
62  
63      /** have to match the CHECKSUM_IDS */
64      private static final String[] CHECKSUM_ALGORITHMS = { "MD5", "SHA-1" };
65  
66      @Requirement
67      private Logger logger;
68  
69      @Requirement
70      private PlexusContainer container;
71  
72      @Requirement
73      private UpdateCheckManager updateCheckManager;
74  
75      //
76      // Retriever
77      //
78      public void getArtifact( Artifact artifact, ArtifactRepository repository, TransferListener downloadMonitor,
79                               boolean force )
80          throws TransferFailedException, ResourceDoesNotExistException
81      {
82          String remotePath = repository.pathOf( artifact );
83  
84          ArtifactRepositoryPolicy policy = artifact.isSnapshot() ? repository.getSnapshots() : repository.getReleases();
85  
86          if ( !policy.isEnabled() )
87          {
88              logger.debug( "Skipping disabled repository " + repository.getId() + " for resolution of "
89                  + artifact.getId() );
90          }
91          else if ( artifact.isSnapshot() || !artifact.getFile().exists() )
92          {
93              if ( force || updateCheckManager.isUpdateRequired( artifact, repository ) )
94              {
95                  logger.debug( "Trying repository " + repository.getId() + " for resolution of " + artifact.getId()
96                      + " from " + remotePath );
97  
98                  try
99                  {
100                     getRemoteFile( repository, artifact.getFile(), remotePath, downloadMonitor,
101                                    policy.getChecksumPolicy(), false );
102 
103                     updateCheckManager.touch( artifact, repository, null );
104                 }
105                 catch ( ResourceDoesNotExistException e )
106                 {
107                     updateCheckManager.touch( artifact, repository, null );
108                     throw e;
109                 }
110                 catch ( TransferFailedException e )
111                 {
112                     String error = ( e.getMessage() != null ) ? e.getMessage() : e.getClass().getSimpleName();
113                     updateCheckManager.touch( artifact, repository, error );
114                     throw e;
115                 }
116 
117                 logger.debug( "  Artifact " + artifact.getId() + " resolved to " + artifact.getFile() );
118 
119                 artifact.setResolved( true );
120             }
121             else if ( !artifact.getFile().exists() )
122             {
123                 String error = updateCheckManager.getError( artifact, repository );
124                 if ( error != null )
125                 {
126                     throw new TransferFailedException( "Failure to resolve " + remotePath + " from "
127                         + repository.getUrl() + " was cached in the local repository. "
128                         + "Resolution will not be reattempted until the update interval of " + repository.getId()
129                         + " has elapsed or updates are forced. Original error: " + error );
130                 }
131                 else
132                 {
133                     throw new ResourceDoesNotExistException( "Failure to resolve " + remotePath + " from "
134                         + repository.getUrl() + " was cached in the local repository. "
135                         + "Resolution will not be reattempted until the update interval of " + repository.getId()
136                         + " has elapsed or updates are forced." );
137                 }
138             }
139         }
140     }
141 
142     public void getArtifact( Artifact artifact, List<ArtifactRepository> remoteRepositories,
143                              TransferListener downloadMonitor, boolean force )
144         throws TransferFailedException, ResourceDoesNotExistException
145     {
146         TransferFailedException tfe = null;
147 
148         for ( ArtifactRepository repository : remoteRepositories )
149         {
150             try
151             {
152                 getArtifact( artifact, repository, downloadMonitor, force );
153 
154                 if ( artifact.isResolved() )
155                 {
156                     artifact.setRepository( repository );
157                     break;
158                 }
159             }
160             catch ( ResourceDoesNotExistException e )
161             {
162                 // This one we will eat when looking through remote repositories
163                 // because we want to cycle through them all before squawking.
164 
165                 logger.debug( "Unable to find artifact " + artifact.getId() + " in repository " + repository.getId()
166                     + " (" + repository.getUrl() + ")", e );
167             }
168             catch ( TransferFailedException e )
169             {
170                 tfe = e;
171 
172                 String msg =
173                     "Unable to get artifact " + artifact.getId() + " from repository " + repository.getId() + " ("
174                         + repository.getUrl() + "): " + e.getMessage();
175                 if ( logger.isDebugEnabled() )
176                 {
177                     logger.warn( msg, e );
178                 }
179                 else
180                 {
181                     logger.warn( msg );
182                 }
183             }
184         }
185 
186         // if it already exists locally we were just trying to force it - ignore the update
187         if ( !artifact.getFile().exists() )
188         {
189             if ( tfe != null )
190             {
191                 throw tfe;
192             }
193             else
194             {
195                 throw new ResourceDoesNotExistException( "Unable to download the artifact from any repository" );
196             }
197         }
198     }
199 
200     public void getArtifactMetadata( ArtifactMetadata metadata, ArtifactRepository repository, File destination,
201                                      String checksumPolicy )
202         throws TransferFailedException, ResourceDoesNotExistException
203     {
204         String remotePath = repository.pathOfRemoteRepositoryMetadata( metadata );
205 
206         getRemoteFile( repository, destination, remotePath, null, checksumPolicy, true );
207     }
208 
209     public void getArtifactMetadataFromDeploymentRepository( ArtifactMetadata metadata, ArtifactRepository repository,
210                                                              File destination, String checksumPolicy )
211         throws TransferFailedException, ResourceDoesNotExistException
212     {
213         String remotePath = repository.pathOfRemoteRepositoryMetadata( metadata );
214 
215         getRemoteFile( repository, destination, remotePath, null, checksumPolicy, true );
216     }
217 
218     /**
219      * Deal with connecting to a wagon repository taking into account authentication and proxies.
220      *
221      * @param wagon
222      * @param repository
223      * @throws ConnectionException
224      * @throws AuthenticationException
225      */
226     private void connectWagon( Wagon wagon, ArtifactRepository repository )
227         throws ConnectionException, AuthenticationException
228     {
229         if ( repository.getProxy() != null && logger.isDebugEnabled() )
230         {
231             logger.debug( "Using proxy " + repository.getProxy().getHost() + ":" + repository.getProxy().getPort()
232                 + " for " + repository.getUrl() );
233         }
234 
235         if ( repository.getAuthentication() != null && repository.getProxy() != null )
236         {
237             wagon.connect( new Repository( repository.getId(), repository.getUrl() ), authenticationInfo( repository ),
238                            proxyInfo( repository ) );
239         }
240         else if ( repository.getAuthentication() != null )
241         {
242             wagon.connect( new Repository( repository.getId(), repository.getUrl() ),
243                            authenticationInfo( repository ) );
244         }
245         else if ( repository.getProxy() != null )
246         {
247             wagon.connect( new Repository( repository.getId(), repository.getUrl() ), proxyInfo( repository ) );
248         }
249         else
250         {
251             wagon.connect( new Repository( repository.getId(), repository.getUrl() ) );
252         }
253     }
254 
255     private AuthenticationInfo authenticationInfo( ArtifactRepository repository )
256     {
257         AuthenticationInfo ai = new AuthenticationInfo();
258         ai.setUserName( repository.getAuthentication().getUsername() );
259         ai.setPassword( repository.getAuthentication().getPassword() );
260         return ai;
261     }
262 
263     private ProxyInfo proxyInfo( ArtifactRepository repository )
264     {
265         ProxyInfo proxyInfo = new ProxyInfo();
266         proxyInfo.setHost( repository.getProxy().getHost() );
267         proxyInfo.setType( repository.getProxy().getProtocol() );
268         proxyInfo.setPort( repository.getProxy().getPort() );
269         proxyInfo.setNonProxyHosts( repository.getProxy().getNonProxyHosts() );
270         proxyInfo.setUserName( repository.getProxy().getUserName() );
271         proxyInfo.setPassword( repository.getProxy().getPassword() );
272         return proxyInfo;
273     }
274 
275     public void getRemoteFile( ArtifactRepository repository, File destination, String remotePath,
276                                TransferListener downloadMonitor, String checksumPolicy, boolean force )
277         throws TransferFailedException, ResourceDoesNotExistException
278     {
279         String protocol = repository.getProtocol();
280 
281         Wagon wagon;
282 
283         try
284         {
285             wagon = getWagon( protocol );
286         }
287         catch ( UnsupportedProtocolException e )
288         {
289             throw new TransferFailedException( "Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e );
290         }
291 
292         if ( downloadMonitor != null )
293         {
294             wagon.addTransferListener( downloadMonitor );
295         }
296 
297         File temp = new File( destination + ".tmp" );
298 
299         temp.deleteOnExit();
300 
301         boolean downloaded = false;
302 
303         try
304         {
305             connectWagon( wagon, repository );
306 
307             boolean firstRun = true;
308             boolean retry = true;
309 
310             // this will run at most twice. The first time, the firstRun flag is turned off, and if the retry flag
311             // is set on the first run, it will be turned off and not re-set on the second try. This is because the
312             // only way the retry flag can be set is if ( firstRun == true ).
313             while ( firstRun || retry )
314             {
315                 ChecksumObserver md5ChecksumObserver = null;
316                 ChecksumObserver sha1ChecksumObserver = null;
317                 try
318                 {
319                     // TODO: configure on repository
320                     int i = 0;
321 
322                     md5ChecksumObserver = addChecksumObserver( wagon, CHECKSUM_ALGORITHMS[i++] );
323                     sha1ChecksumObserver = addChecksumObserver( wagon, CHECKSUM_ALGORITHMS[i++] );
324 
325                     // reset the retry flag.
326                     retry = false;
327 
328                     // This should take care of creating destination directory now on
329                     if ( destination.exists() && !force )
330                     {
331                         try
332                         {
333                             downloaded = wagon.getIfNewer( remotePath, temp, destination.lastModified() );
334 
335                             if ( !downloaded )
336                             {
337                                 // prevent additional checks of this artifact until it expires again
338                                 destination.setLastModified( System.currentTimeMillis() );
339                             }
340                         }
341                         catch ( UnsupportedOperationException e )
342                         {
343                             // older wagons throw this. Just get() instead
344                             wagon.get( remotePath, temp );
345 
346                             downloaded = true;
347                         }
348                     }
349                     else
350                     {
351                         wagon.get( remotePath, temp );
352                         downloaded = true;
353                     }
354                 }
355                 finally
356                 {
357                     wagon.removeTransferListener( md5ChecksumObserver );
358                     wagon.removeTransferListener( sha1ChecksumObserver );
359                 }
360 
361                 if ( downloaded )
362                 {
363                     // keep the checksum files from showing up on the download monitor...
364                     if ( downloadMonitor != null )
365                     {
366                         wagon.removeTransferListener( downloadMonitor );
367                     }
368 
369                     // try to verify the SHA-1 checksum for this file.
370                     try
371                     {
372                         verifyChecksum( sha1ChecksumObserver, destination, temp, remotePath, ".sha1", wagon );
373                     }
374                     catch ( ChecksumFailedException e )
375                     {
376                         // if we catch a ChecksumFailedException, it means the transfer/read succeeded, but the checksum
377                         // doesn't match. This could be a problem with the server (ibiblio HTTP-200 error page), so we'll
378                         // try this up to two times. On the second try, we'll handle it as a bona-fide error, based on the
379                         // repository's checksum checking policy.
380                         if ( firstRun )
381                         {
382                             logger.warn( "*** CHECKSUM FAILED - " + e.getMessage() + " - RETRYING" );
383                             retry = true;
384                         }
385                         else
386                         {
387                             handleChecksumFailure( checksumPolicy, e.getMessage(), e.getCause() );
388                         }
389                     }
390                     catch ( ResourceDoesNotExistException sha1TryException )
391                     {
392                         logger.debug( "SHA1 not found, trying MD5: " + sha1TryException.getMessage() );
393 
394                         // if this IS NOT a ChecksumFailedException, it was a problem with transfer/read of the checksum
395                         // file...we'll try again with the MD5 checksum.
396                         try
397                         {
398                             verifyChecksum( md5ChecksumObserver, destination, temp, remotePath, ".md5", wagon );
399                         }
400                         catch ( ChecksumFailedException e )
401                         {
402                             // if we also fail to verify based on the MD5 checksum, and the checksum transfer/read
403                             // succeeded, then we need to determine whether to retry or handle it as a failure.
404                             if ( firstRun )
405                             {
406                                 retry = true;
407                             }
408                             else
409                             {
410                                 handleChecksumFailure( checksumPolicy, e.getMessage(), e.getCause() );
411                             }
412                         }
413                         catch ( ResourceDoesNotExistException md5TryException )
414                         {
415                             // this was a failed transfer, and we don't want to retry.
416                             handleChecksumFailure( checksumPolicy, "Error retrieving checksum file for " + remotePath,
417                                                    md5TryException );
418                         }
419                     }
420 
421                     // reinstate the download monitor...
422                     if ( downloadMonitor != null )
423                     {
424                         wagon.addTransferListener( downloadMonitor );
425                     }
426                 }
427 
428                 // unset the firstRun flag, so we don't get caught in an infinite loop...
429                 firstRun = false;
430             }
431         }
432         catch ( ConnectionException e )
433         {
434             throw new TransferFailedException( "Connection failed: " + e.getMessage(), e );
435         }
436         catch ( AuthenticationException e )
437         {
438             throw new TransferFailedException( "Authentication failed: " + e.getMessage(), e );
439         }
440         catch ( AuthorizationException e )
441         {
442             throw new TransferFailedException( "Authorization failed: " + e.getMessage(), e );
443         }
444         finally
445         {
446             // Remove remaining TransferListener instances (checksum handlers removed in above finally clause)
447             if ( downloadMonitor != null )
448             {
449                 wagon.removeTransferListener( downloadMonitor );
450             }
451 
452             disconnectWagon( wagon );
453 
454             releaseWagon( protocol, wagon );
455         }
456 
457         if ( downloaded )
458         {
459             if ( !temp.exists() )
460             {
461                 throw new ResourceDoesNotExistException( "Downloaded file does not exist: " + temp );
462             }
463 
464             // The temporary file is named destination + ".tmp" and is done this way to ensure
465             // that the temporary file is in the same file system as the destination because the
466             // File.renameTo operation doesn't really work across file systems.
467             // So we will attempt to do a File.renameTo for efficiency and atomicity, if this fails
468             // then we will use a brute force copy and delete the temporary file.
469 
470             if ( !temp.renameTo( destination ) )
471             {
472                 try
473                 {
474                     FileUtils.copyFile( temp, destination );
475 
476                     temp.delete();
477                 }
478                 catch ( IOException e )
479                 {
480                     throw new TransferFailedException( "Error copying temporary file to the final destination: "
481                         + e.getMessage(), e );
482                 }
483             }
484         }
485     }
486 
487     //
488     // Publisher
489     //
490     public void putArtifact( File source, Artifact artifact, ArtifactRepository deploymentRepository,
491                              TransferListener downloadMonitor )
492         throws TransferFailedException
493     {
494         putRemoteFile( deploymentRepository, source, deploymentRepository.pathOf( artifact ), downloadMonitor );
495     }
496 
497     public void putArtifactMetadata( File source, ArtifactMetadata artifactMetadata, ArtifactRepository repository )
498         throws TransferFailedException
499     {
500         logger.info( "Uploading " + artifactMetadata );
501         putRemoteFile( repository, source, repository.pathOfRemoteRepositoryMetadata( artifactMetadata ), null );
502     }
503 
504     public void putRemoteFile( ArtifactRepository repository, File source, String remotePath,
505                                TransferListener downloadMonitor )
506         throws TransferFailedException
507     {
508         String protocol = repository.getProtocol();
509 
510         Wagon wagon;
511         try
512         {
513             wagon = getWagon( protocol );
514         }
515         catch ( UnsupportedProtocolException e )
516         {
517             throw new TransferFailedException( "Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e );
518         }
519 
520         if ( downloadMonitor != null )
521         {
522             wagon.addTransferListener( downloadMonitor );
523         }
524 
525         Map<String, ChecksumObserver> checksums = new HashMap<String, ChecksumObserver>( 2 );
526 
527         Map<String, String> sums = new HashMap<String, String>( 2 );
528 
529         // TODO: configure these on the repository
530         for ( int i = 0; i < CHECKSUM_IDS.length; i++ )
531         {
532             checksums.put( CHECKSUM_IDS[i], addChecksumObserver( wagon, CHECKSUM_ALGORITHMS[i] ) );
533         }
534 
535         List<File> temporaryFiles = new ArrayList<File>();
536 
537         try
538         {
539             try
540             {
541                 connectWagon( wagon, repository );
542 
543                 wagon.put( source, remotePath );
544             }
545             finally
546             {
547                 if ( downloadMonitor != null )
548                 {
549                     wagon.removeTransferListener( downloadMonitor );
550                 }
551             }
552 
553             // Pre-store the checksums as any future puts will overwrite them
554             for ( String extension : checksums.keySet() )
555             {
556                 ChecksumObserver observer = checksums.get( extension );
557                 sums.put( extension, observer.getActualChecksum() );
558             }
559 
560             // We do this in here so we can checksum the artifact metadata too, otherwise it could be metadata itself
561             for ( String extension : checksums.keySet() )
562             {
563                 // TODO: shouldn't need a file intermediatary - improve wagon to take a stream
564                 File temp = File.createTempFile( "maven-artifact", null );
565                 temp.deleteOnExit();
566                 FileUtils.fileWrite( temp.getAbsolutePath(), "UTF-8", sums.get( extension ) );
567 
568                 temporaryFiles.add( temp );
569                 wagon.put( temp, remotePath + "." + extension );
570             }
571         }
572         catch ( ConnectionException e )
573         {
574             throw new TransferFailedException( "Connection failed: " + e.getMessage(), e );
575         }
576         catch ( AuthenticationException e )
577         {
578             throw new TransferFailedException( "Authentication failed: " + e.getMessage(), e );
579         }
580         catch ( AuthorizationException e )
581         {
582             throw new TransferFailedException( "Authorization failed: " + e.getMessage(), e );
583         }
584         catch ( ResourceDoesNotExistException e )
585         {
586             throw new TransferFailedException( "Resource to deploy not found: " + e.getMessage(), e );
587         }
588         catch ( IOException e )
589         {
590             throw new TransferFailedException( "Error creating temporary file for deployment: " + e.getMessage(), e );
591         }
592         finally
593         {
594             // MNG-4543
595             cleanupTemporaryFiles( temporaryFiles );
596 
597             // Remove every checksum listener
598             for ( String aCHECKSUM_IDS : CHECKSUM_IDS )
599             {
600                 TransferListener checksumListener = checksums.get( aCHECKSUM_IDS );
601                 if ( checksumListener != null )
602                 {
603                     wagon.removeTransferListener( checksumListener );
604                 }
605             }
606 
607             disconnectWagon( wagon );
608 
609             releaseWagon( protocol, wagon );
610         }
611     }
612 
613     private void cleanupTemporaryFiles( List<File> files )
614     {
615         for ( File file : files )
616         {
617             // really don't care if it failed here only log warning
618             try
619             {
620                 file.delete();
621             }
622             catch ( Exception e )
623             {
624                 logger.warn( "skip failed to delete temporary file : " + file.getAbsolutePath() + " , message "
625                     + e.getMessage() );
626             }
627         }
628 
629     }
630 
631     private ChecksumObserver addChecksumObserver( Wagon wagon, String algorithm )
632         throws TransferFailedException
633     {
634         try
635         {
636             ChecksumObserver checksumObserver = new ChecksumObserver( algorithm );
637             wagon.addTransferListener( checksumObserver );
638             return checksumObserver;
639         }
640         catch ( NoSuchAlgorithmException e )
641         {
642             throw new TransferFailedException( "Unable to add checksum for unsupported algorithm " + algorithm, e );
643         }
644     }
645 
646     private void handleChecksumFailure( String checksumPolicy, String message, Throwable cause )
647         throws ChecksumFailedException
648     {
649         if ( ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL.equals( checksumPolicy ) )
650         {
651             throw new ChecksumFailedException( message, cause );
652         }
653         else if ( !ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals( checksumPolicy ) )
654         {
655             // warn if it is set to anything other than ignore
656             logger.warn( "*** CHECKSUM FAILED - " + message + " - IGNORING" );
657         }
658         // otherwise it is ignore
659     }
660 
661     private void verifyChecksum( ChecksumObserver checksumObserver, File destination, File tempDestination,
662                                  String remotePath, String checksumFileExtension, Wagon wagon )
663         throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException
664     {
665         try
666         {
667             // grab it first, because it's about to change...
668             String actualChecksum = checksumObserver.getActualChecksum();
669 
670             File tempChecksumFile = new File( tempDestination + checksumFileExtension + ".tmp" );
671             tempChecksumFile.deleteOnExit();
672             wagon.get( remotePath + checksumFileExtension, tempChecksumFile );
673 
674             String expectedChecksum = FileUtils.fileRead( tempChecksumFile, "UTF-8" );
675 
676             // remove whitespaces at the end
677             expectedChecksum = expectedChecksum.trim();
678 
679             // check for 'ALGO (name) = CHECKSUM' like used by openssl
680             if ( expectedChecksum.regionMatches( true, 0, "MD", 0, 2 )
681                 || expectedChecksum.regionMatches( true, 0, "SHA", 0, 3 ) )
682             {
683                 int lastSpacePos = expectedChecksum.lastIndexOf( ' ' );
684                 expectedChecksum = expectedChecksum.substring( lastSpacePos + 1 );
685             }
686             else
687             {
688                 // remove everything after the first space (if available)
689                 int spacePos = expectedChecksum.indexOf( ' ' );
690 
691                 if ( spacePos != -1 )
692                 {
693                     expectedChecksum = expectedChecksum.substring( 0, spacePos );
694                 }
695             }
696             if ( expectedChecksum.equalsIgnoreCase( actualChecksum ) )
697             {
698                 File checksumFile = new File( destination + checksumFileExtension );
699                 if ( checksumFile.exists() )
700                 {
701                     checksumFile.delete();
702                 }
703                 FileUtils.copyFile( tempChecksumFile, checksumFile );
704                 tempChecksumFile.delete();
705             }
706             else
707             {
708                 throw new ChecksumFailedException( "Checksum failed on download: local = '" + actualChecksum
709                     + "'; remote = '" + expectedChecksum + "'" );
710             }
711         }
712         catch ( IOException e )
713         {
714             throw new ChecksumFailedException( "Invalid checksum file", e );
715         }
716     }
717 
718     private void disconnectWagon( Wagon wagon )
719     {
720         try
721         {
722             wagon.disconnect();
723         }
724         catch ( ConnectionException e )
725         {
726             logger.error( "Problem disconnecting from wagon - ignoring: " + e.getMessage() );
727         }
728     }
729 
730     private void releaseWagon( String protocol, Wagon wagon )
731     {
732         try
733         {
734             container.release( wagon );
735         }
736         catch ( ComponentLifecycleException e )
737         {
738             logger.error( "Problem releasing wagon - ignoring: " + e.getMessage() );
739             logger.debug( "", e );
740         }
741     }
742 
743     @Deprecated
744     public Wagon getWagon( Repository repository )
745         throws UnsupportedProtocolException
746     {
747         return getWagon( repository.getProtocol() );
748     }
749 
750     @Deprecated
751     public Wagon getWagon( String protocol )
752         throws UnsupportedProtocolException
753     {
754         if ( protocol == null )
755         {
756             throw new UnsupportedProtocolException( "Unspecified protocol" );
757         }
758 
759         String hint = protocol.toLowerCase( java.util.Locale.ENGLISH );
760 
761         Wagon wagon;
762         try
763         {
764             wagon = container.lookup( Wagon.class, hint );
765         }
766         catch ( ComponentLookupException e )
767         {
768             throw new UnsupportedProtocolException( "Cannot find wagon which supports the requested protocol: "
769                 + protocol, e );
770         }
771 
772         return wagon;
773     }
774 
775 }