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