001    package org.apache.maven.repository.legacy;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.File;
023    import java.io.IOException;
024    import java.security.NoSuchAlgorithmException;
025    import java.util.ArrayList;
026    import java.util.HashMap;
027    import java.util.List;
028    import java.util.Map;
029    
030    import org.apache.maven.artifact.Artifact;
031    import org.apache.maven.artifact.metadata.ArtifactMetadata;
032    import org.apache.maven.artifact.repository.ArtifactRepository;
033    import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
034    import org.apache.maven.wagon.ConnectionException;
035    import org.apache.maven.wagon.ResourceDoesNotExistException;
036    import org.apache.maven.wagon.TransferFailedException;
037    import org.apache.maven.wagon.UnsupportedProtocolException;
038    import org.apache.maven.wagon.Wagon;
039    import org.apache.maven.wagon.authentication.AuthenticationException;
040    import org.apache.maven.wagon.authentication.AuthenticationInfo;
041    import org.apache.maven.wagon.authorization.AuthorizationException;
042    import org.apache.maven.wagon.events.TransferListener;
043    import org.apache.maven.wagon.observers.ChecksumObserver;
044    import org.apache.maven.wagon.proxy.ProxyInfo;
045    import org.apache.maven.wagon.repository.Repository;
046    import org.codehaus.plexus.PlexusContainer;
047    import org.codehaus.plexus.component.annotations.Component;
048    import org.codehaus.plexus.component.annotations.Requirement;
049    import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
050    import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
051    import org.codehaus.plexus.logging.Logger;
052    import org.codehaus.plexus.util.FileUtils;
053    
054    //TODO: remove the update check manager
055    //TODO: separate into retriever and publisher
056    //TODO: remove hardcoding of checksum logic
057    @Component( role = WagonManager.class )
058    public class DefaultWagonManager
059        implements WagonManager
060    {
061        private static final String[] CHECKSUM_IDS = { "md5", "sha1" };
062    
063        /** have to match the CHECKSUM_IDS */
064        private static final String[] CHECKSUM_ALGORITHMS = { "MD5", "SHA-1" };
065    
066        @Requirement
067        private Logger logger;
068    
069        @Requirement
070        private PlexusContainer container;
071    
072        @Requirement
073        private UpdateCheckManager updateCheckManager;
074    
075        //
076        // Retriever
077        //
078        public void getArtifact( Artifact artifact, ArtifactRepository repository, TransferListener downloadMonitor,
079                                 boolean force )
080            throws TransferFailedException, ResourceDoesNotExistException
081        {
082            String remotePath = repository.pathOf( artifact );
083    
084            ArtifactRepositoryPolicy policy = artifact.isSnapshot() ? repository.getSnapshots() : repository.getReleases();
085    
086            if ( !policy.isEnabled() )
087            {
088                logger.debug( "Skipping disabled repository " + repository.getId() + " for resolution of "
089                    + artifact.getId() );
090            }
091            else if ( artifact.isSnapshot() || !artifact.getFile().exists() )
092            {
093                if ( force || updateCheckManager.isUpdateRequired( artifact, repository ) )
094                {
095                    logger.debug( "Trying repository " + repository.getId() + " for resolution of " + artifact.getId()
096                        + " from " + remotePath );
097    
098                    try
099                    {
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    }