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                     temp.delete();
509                 }
510                 catch ( IOException e )
511                 {
512                     throw new TransferFailedException( "Error copying temporary file to the final destination: "
513                         + e.getMessage(), e );
514                 }
515             }
516         }
517     }
518 
519     //
520     // Publisher
521     //
522     public void putArtifact( File source, Artifact artifact, ArtifactRepository deploymentRepository,
523                              TransferListener downloadMonitor )
524         throws TransferFailedException
525     {
526         putRemoteFile( deploymentRepository, source, deploymentRepository.pathOf( artifact ), downloadMonitor );
527     }
528 
529     public void putArtifactMetadata( File source, ArtifactMetadata artifactMetadata, ArtifactRepository repository )
530         throws TransferFailedException
531     {
532         logger.info( "Uploading " + artifactMetadata );
533         putRemoteFile( repository, source, repository.pathOfRemoteRepositoryMetadata( artifactMetadata ), null );
534     }
535 
536     public void putRemoteFile( ArtifactRepository repository, File source, String remotePath,
537                                TransferListener downloadMonitor )
538         throws TransferFailedException
539     {
540         String protocol = repository.getProtocol();
541 
542         Wagon wagon;
543         try
544         {
545             wagon = getWagon( protocol );
546         }
547         catch ( UnsupportedProtocolException e )
548         {
549             throw new TransferFailedException( "Unsupported Protocol: '" + protocol + "': " + e.getMessage(), e );
550         }
551 
552         if ( downloadMonitor != null )
553         {
554             wagon.addTransferListener( downloadMonitor );
555         }
556 
557         Map<String, ChecksumObserver> checksums = new HashMap<String, ChecksumObserver>( 2 );
558 
559         Map<String, String> sums = new HashMap<String, String>( 2 );
560 
561         // TODO: configure these on the repository
562         for ( int i = 0; i < CHECKSUM_IDS.length; i++ )
563         {
564             checksums.put( CHECKSUM_IDS[i], addChecksumObserver( wagon, CHECKSUM_ALGORITHMS[i] ) );
565         }
566 
567         List<File> temporaryFiles = new ArrayList<File>();
568 
569         try
570         {
571             try
572             {
573                 connectWagon( wagon, repository );
574 
575                 wagon.put( source, remotePath );
576             }
577             finally
578             {
579                 if ( downloadMonitor != null )
580                 {
581                     wagon.removeTransferListener( downloadMonitor );
582                 }
583             }
584 
585             // Pre-store the checksums as any future puts will overwrite them
586             for ( String extension : checksums.keySet() )
587             {
588                 ChecksumObserver observer = checksums.get( extension );
589                 sums.put( extension, observer.getActualChecksum() );
590             }
591 
592             // We do this in here so we can checksum the artifact metadata too, otherwise it could be metadata itself
593             for ( String extension : checksums.keySet() )
594             {
595                 // TODO: shouldn't need a file intermediatary - improve wagon to take a stream
596                 File temp = File.createTempFile( "maven-artifact", null );
597                 temp.deleteOnExit();
598                 FileUtils.fileWrite( temp.getAbsolutePath(), "UTF-8", sums.get( extension ) );
599 
600                 temporaryFiles.add( temp );
601                 wagon.put( temp, remotePath + "." + extension );
602             }
603         }
604         catch ( ConnectionException e )
605         {
606             throw new TransferFailedException( "Connection failed: " + e.getMessage(), e );
607         }
608         catch ( AuthenticationException e )
609         {
610             throw new TransferFailedException( "Authentication failed: " + e.getMessage(), e );
611         }
612         catch ( AuthorizationException e )
613         {
614             throw new TransferFailedException( "Authorization failed: " + e.getMessage(), e );
615         }
616         catch ( ResourceDoesNotExistException e )
617         {
618             throw new TransferFailedException( "Resource to deploy not found: " + e.getMessage(), e );
619         }
620         catch ( IOException e )
621         {
622             throw new TransferFailedException( "Error creating temporary file for deployment: " + e.getMessage(), e );
623         }
624         finally
625         {
626             // MNG-4543
627             cleanupTemporaryFiles( temporaryFiles );
628 
629             // Remove every checksum listener
630             for ( String aCHECKSUM_IDS : CHECKSUM_IDS )
631             {
632                 TransferListener checksumListener = checksums.get( aCHECKSUM_IDS );
633                 if ( checksumListener != null )
634                 {
635                     wagon.removeTransferListener( checksumListener );
636                 }
637             }
638 
639             disconnectWagon( wagon );
640 
641             releaseWagon( protocol, wagon );
642         }
643     }
644 
645     private void cleanupTemporaryFiles( List<File> files )
646     {
647         for ( File file : files )
648         {
649             // really don't care if it failed here only log warning
650             try
651             {
652                 file.delete();
653             }
654             catch ( Exception e )
655             {
656                 logger.warn( "skip failed to delete temporary file : " + file.getAbsolutePath() + " , message "
657                     + e.getMessage() );
658             }
659         }
660 
661     }
662 
663     private ChecksumObserver addChecksumObserver( Wagon wagon, String algorithm )
664         throws TransferFailedException
665     {
666         try
667         {
668             ChecksumObserver checksumObserver = new ChecksumObserver( algorithm );
669             wagon.addTransferListener( checksumObserver );
670             return checksumObserver;
671         }
672         catch ( NoSuchAlgorithmException e )
673         {
674             throw new TransferFailedException( "Unable to add checksum for unsupported algorithm " + algorithm, e );
675         }
676     }
677 
678     private void handleChecksumFailure( String checksumPolicy, String message, Throwable cause )
679         throws ChecksumFailedException
680     {
681         if ( ArtifactRepositoryPolicy.CHECKSUM_POLICY_FAIL.equals( checksumPolicy ) )
682         {
683             throw new ChecksumFailedException( message, cause );
684         }
685         else if ( !ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE.equals( checksumPolicy ) )
686         {
687             // warn if it is set to anything other than ignore
688             logger.warn( "*** CHECKSUM FAILED - " + message + " - IGNORING" );
689         }
690         // otherwise it is ignore
691     }
692 
693     private void verifyChecksum( ChecksumObserver checksumObserver, File destination, File tempDestination,
694                                  String remotePath, String checksumFileExtension, Wagon wagon )
695         throws ResourceDoesNotExistException, TransferFailedException, AuthorizationException
696     {
697         try
698         {
699             // grab it first, because it's about to change...
700             String actualChecksum = checksumObserver.getActualChecksum();
701 
702             File tempChecksumFile = new File( tempDestination + checksumFileExtension + ".tmp" );
703             tempChecksumFile.deleteOnExit();
704             wagon.get( remotePath + checksumFileExtension, tempChecksumFile );
705 
706             String expectedChecksum = FileUtils.fileRead( tempChecksumFile, "UTF-8" );
707 
708             // remove whitespaces at the end
709             expectedChecksum = expectedChecksum.trim();
710 
711             // check for 'ALGO (name) = CHECKSUM' like used by openssl
712             if ( expectedChecksum.regionMatches( true, 0, "MD", 0, 2 )
713                 || expectedChecksum.regionMatches( true, 0, "SHA", 0, 3 ) )
714             {
715                 int lastSpacePos = expectedChecksum.lastIndexOf( ' ' );
716                 expectedChecksum = expectedChecksum.substring( lastSpacePos + 1 );
717             }
718             else
719             {
720                 // remove everything after the first space (if available)
721                 int spacePos = expectedChecksum.indexOf( ' ' );
722 
723                 if ( spacePos != -1 )
724                 {
725                     expectedChecksum = expectedChecksum.substring( 0, spacePos );
726                 }
727             }
728             if ( expectedChecksum.equalsIgnoreCase( actualChecksum ) )
729             {
730                 File checksumFile = new File( destination + checksumFileExtension );
731                 if ( checksumFile.exists() )
732                 {
733                     checksumFile.delete();
734                 }
735                 FileUtils.copyFile( tempChecksumFile, checksumFile );
736                 tempChecksumFile.delete();
737             }
738             else
739             {
740                 throw new ChecksumFailedException( "Checksum failed on download: local = '" + actualChecksum
741                     + "'; remote = '" + expectedChecksum + "'" );
742             }
743         }
744         catch ( IOException e )
745         {
746             throw new ChecksumFailedException( "Invalid checksum file", e );
747         }
748     }
749 
750     private void disconnectWagon( Wagon wagon )
751     {
752         try
753         {
754             wagon.disconnect();
755         }
756         catch ( ConnectionException e )
757         {
758             logger.error( "Problem disconnecting from wagon - ignoring: " + e.getMessage() );
759         }
760     }
761 
762     private void releaseWagon( String protocol, Wagon wagon )
763     {
764         try
765         {
766             container.release( wagon );
767         }
768         catch ( ComponentLifecycleException e )
769         {
770             logger.error( "Problem releasing wagon - ignoring: " + e.getMessage() );
771             logger.debug( "", e );
772         }
773     }
774 
775     @Deprecated
776     public Wagon getWagon( Repository repository )
777         throws UnsupportedProtocolException
778     {
779         return getWagon( repository.getProtocol() );
780     }
781 
782     @Deprecated
783     public Wagon getWagon( String protocol )
784         throws UnsupportedProtocolException
785     {
786         if ( protocol == null )
787         {
788             throw new UnsupportedProtocolException( "Unspecified protocol" );
789         }
790 
791         String hint = protocol.toLowerCase( java.util.Locale.ENGLISH );
792 
793         Wagon wagon;
794         try
795         {
796             wagon = container.lookup( Wagon.class, hint );
797         }
798         catch ( ComponentLookupException e )
799         {
800             throw new UnsupportedProtocolException( "Cannot find wagon which supports the requested protocol: "
801                 + protocol, e );
802         }
803         
804         return wagon;
805     }
806 
807 }