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