Author: ltheussl
Date: Fri May 19 14:55:30 2006
New Revision: 407919

URL: http://svn.apache.org/viewvc?rev=407919&view=rev
Log:
PR: MAVEN-121, MAVEN-1080, MAVEN-1648
Download md5 sums of artifacts and verify checksums.

Modified:
    
maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/ChecksumVerificationException.java
    
maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/DependencyVerifier.java

Modified: 
maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/ChecksumVerificationException.java
URL: 
http://svn.apache.org/viewvc/maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/ChecksumVerificationException.java?rev=407919&r1=407918&r2=407919&view=diff
==============================================================================
--- 
maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/ChecksumVerificationException.java
 (original)
+++ 
maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/ChecksumVerificationException.java
 Fri May 19 14:55:30 2006
@@ -36,4 +36,16 @@
     {
         super( message );
     }
+
+    /**
+     * Constructs a ChecksumVerificationException with the specified detail 
message
+     * and the causing Throwable.
+     * @param message Detailed message.
+     * @param cause The Throwable.
+     */
+    public ChecksumVerificationException( String message, Throwable cause )
+    {
+        super( message, cause );
+    }
+
 }

Modified: 
maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/DependencyVerifier.java
URL: 
http://svn.apache.org/viewvc/maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/DependencyVerifier.java?rev=407919&r1=407918&r2=407919&view=diff
==============================================================================
--- 
maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/DependencyVerifier.java
 (original)
+++ 
maven/maven-1/core/trunk/src/java/org/apache/maven/verifier/DependencyVerifier.java
 Fri May 19 14:55:30 2006
@@ -18,11 +18,13 @@
  */
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.security.NoSuchAlgorithmException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -35,12 +37,16 @@
 import org.apache.maven.util.ConsoleDownloadMeter;
 import org.apache.maven.wagon.ConnectionException;
 import org.apache.maven.wagon.ResourceDoesNotExistException;
+import org.apache.maven.wagon.authorization.AuthorizationException;
 import org.apache.maven.wagon.Wagon;
+import org.apache.maven.wagon.TransferFailedException;
 import org.apache.maven.wagon.events.TransferListener;
+import org.apache.maven.wagon.observers.ChecksumObserver;
 import org.apache.maven.wagon.providers.file.FileWagon;
 import org.apache.maven.wagon.providers.http.HttpWagon;
 import org.apache.maven.wagon.proxy.ProxyInfo;
 import org.apache.maven.wagon.repository.Repository;
+import org.codehaus.plexus.util.FileUtils;
 import org.codehaus.plexus.util.StringUtils;
 
 /**
@@ -92,11 +98,9 @@
         throws RepoConfigException,
         UnsatisfiedDependencyException,
         ChecksumVerificationException
-
     {
         localRepositoryVerifier.verifyLocalRepository();
         satisfyDependencies();
-        //verifyDependencies();
     }
 
     /**
@@ -116,9 +120,10 @@
      * not then download them.
      *
      * @throws UnsatisfiedDependencyException If there are unsatisfied 
dependencies.
+     * @throws ChecksumVerificationException If checksums don't match.
      */
     private void satisfyDependencies()
-        throws UnsatisfiedDependencyException
+        throws UnsatisfiedDependencyException, ChecksumVerificationException
     {
         // Is the remote repository enabled?
         boolean remoteRepoEnabled = 
getProject().getContext().getRemoteRepositoryEnabled().booleanValue();
@@ -256,8 +261,10 @@
     /**
      *  Try and retrieve the dependencies from the remote repository in
      *  order to satisfy the dependencies of the project.
+     * @throws ChecksumVerificationException If checksums don't match.
      */
     private void getDependencies()
+        throws ChecksumVerificationException
     {
         log.debug("Getting failed dependencies: " + failedDependencies);
         for ( Iterator i = failedDependencies.iterator(); i.hasNext();)
@@ -316,8 +323,10 @@
      * and store it at <code>localFile</code>
      * @param artifact the artifact to retrieve from the repositories.
      * @return true if the retrieval succeeds, false otherwise.
+     * @throws ChecksumVerificationException If checksums don't match.
      */
     private boolean getRemoteArtifact( Artifact artifact )
+        throws ChecksumVerificationException
     {
         boolean artifactFound = false;
 
@@ -384,19 +393,120 @@
                 wagon.addTransferListener( listener );
             }
 
+            ChecksumObserver md5ChecksumObserver = null;
+            ChecksumObserver sha1ChecksumObserver = null;
+            try
+            {
+                md5ChecksumObserver = new ChecksumObserver( "MD5" );
+                wagon.addTransferListener( md5ChecksumObserver );
+
+                sha1ChecksumObserver = new ChecksumObserver( "SHA-1" );
+                wagon.addTransferListener( sha1ChecksumObserver );
+            }
+            catch ( NoSuchAlgorithmException e )
+            {
+                throw new ChecksumVerificationException( "Unable to add 
checksum methods: " + e.getMessage(), e );
+            }
+
+            File destination = artifact.getFile();
+            String remotePath = artifact.getUrlPath();
+            File temp = new File( destination + ".tmp" );
+            temp.deleteOnExit();
+
             try
             {
                 wagon.connect( repository, proxyInfo );
-                wagon.getIfNewer( artifact.getUrlPath(), artifact.getFile(), 
artifact.getFile().lastModified() );
 
-                // Artifact was found, continue checking additional remote 
repos (if any)
-                // in case there is a newer version (i.e. snapshots) in 
another repo
-                artifactFound = true;
+                boolean firstRun = true;
+                boolean retry = true;
 
-                if ( !artifact.isSnapshot() )
+                // this will run at most twice. The first time, the firstRun 
flag is turned off, and if the retry flag
+                // is set on the first run, it will be turned off and not 
re-set on the second try. This is because the
+                // only way the retry flag can be set is if ( firstRun == true 
).
+                while ( firstRun || retry )
                 {
-                    break;
+                    // reset the retry flag.
+                    retry = false;
+
+                    wagon.getIfNewer( remotePath, temp, 
destination.lastModified() );
+
+                    // keep the checksum files from showing up on the download 
monitor...
+                    if ( listener != null )
+                    {
+                        wagon.removeTransferListener( listener );
+                    }
+
+                    // try to verify the MD5 checksum for this file.
+                    try
+                    {
+                        verifyChecksum( md5ChecksumObserver, destination, 
temp, remotePath, ".md5", wagon );
+                    }
+                    catch ( ChecksumVerificationException e )
+                    {
+                        // if we catch a ChecksumVerificationException, it 
means the transfer/read succeeded, but the checksum
+                        // doesn't match. This could be a problem with the 
server (ibiblio HTTP-200 error page), so we'll
+                        // try this up to two times. On the second try, we'll 
handle it as a bona-fide error, based on the
+                        // repository's checksum checking policy.
+                        if ( firstRun )
+                        {
+                            log.warn( "*** CHECKSUM FAILED - " + 
e.getMessage() + " - RETRYING" );
+                            retry = true;
+                        }
+                        else
+                        {
+                            throw new ChecksumVerificationException( 
e.getMessage(), e.getCause() );
+                        }
+                    }
+                    catch ( ResourceDoesNotExistException md5TryException )
+                    {
+                        log.debug( "MD5 not found, trying SHA1", 
md5TryException );
+
+                        // if this IS NOT a ChecksumVerificationException, it 
was a problem with transfer/read of the checksum
+                        // file...we'll try again with the SHA-1 checksum.
+                        try
+                        {
+                            verifyChecksum( sha1ChecksumObserver, destination, 
temp, remotePath, ".sha1", wagon );
+                        }
+                        catch ( ChecksumVerificationException e )
+                        {
+                            // if we also fail to verify based on the SHA-1 
checksum, and the checksum transfer/read
+                            // succeeded, then we need to determine whether to 
retry or handle it as a failure.
+                            if ( firstRun )
+                            {
+                                retry = true;
+                            }
+                            else
+                            {
+                                throw new ChecksumVerificationException( 
e.getMessage(), e.getCause() );
+                            }
+                        }
+                        catch ( ResourceDoesNotExistException sha1TryException 
)
+                        {
+                            // this was a failed transfer, and we don't want 
to retry.
+                            throw new ChecksumVerificationException( "Error 
retrieving checksum file for " + remotePath, sha1TryException );
+                        }
+                    }
+
+
+                    // Artifact was found, continue checking additional remote 
repos (if any)
+                    // in case there is a newer version (i.e. snapshots) in 
another repo
+                    artifactFound = true;
+
+                    if ( !artifact.isSnapshot() )
+                    {
+                        break;
+                    }
+
+                    // reinstate the download monitor...
+                    if ( listener != null )
+                    {
+                        wagon.addTransferListener( listener );
+                    }
+
+                    // unset the firstRun flag, so we don't get caught in an 
infinite loop...
+                    firstRun = false;
                 }
+
             }
             catch ( ResourceDoesNotExistException e )
             {
@@ -441,6 +551,39 @@
                     log.debug( "Error disconnecting wagon", e );
                 }
             }
+
+            if ( !temp.exists() )
+            {
+                log.debug( "Downloaded file does not exist: " + temp );
+                artifactFound = false;
+            }
+
+            // The temporary file is named destination + ".tmp" and is done 
this way to ensure
+            // that the temporary file is in the same file system as the 
destination because the
+            // File.renameTo operation doesn't really work across file systems.
+            // So we will attempt to do a File.renameTo for efficiency and 
atomicity, if this fails
+            // then we will use a brute force copy and delete the temporary 
file.
+
+            if ( !temp.renameTo( destination ) )
+            {
+                try
+                {
+                    FileUtils.copyFile( temp, destination );
+                    temp.delete();
+                }
+                catch ( IOException e )
+                {
+                    log.debug( "Error copying temporary file to the final 
destination: " + e.getMessage() );
+                    artifactFound = false;
+                }
+            }
+
+            // don't try another repo if artifact has been found
+            if ( artifactFound )
+            {
+                break;
+            }
+
         }
 
         return artifactFound;
@@ -449,6 +592,58 @@
     // ----------------------------------------------------------------------
     // V E R I F I C A T I O N
     // ----------------------------------------------------------------------
+
+    private void verifyChecksum( ChecksumObserver checksumObserver, File 
destination, File tempDestination, String remotePath,
+                                 String checksumFileExtension, Wagon wagon )
+        throws ResourceDoesNotExistException, TransferFailedException, 
AuthorizationException, ChecksumVerificationException
+    {
+        try
+        {
+            // grab it first, because it's about to change...
+            String actualChecksum = checksumObserver.getActualChecksum();
+
+            File tempChecksumFile = new File( tempDestination + 
checksumFileExtension + ".tmp" );
+            tempChecksumFile.deleteOnExit();
+            wagon.get( remotePath + checksumFileExtension, tempChecksumFile );
+
+            String expectedChecksum = FileUtils.fileRead( tempChecksumFile );
+
+            // remove whitespaces at the end
+            expectedChecksum = expectedChecksum.trim();
+
+            // check for 'MD5 (name) = CHECKSUM'
+            if ( expectedChecksum.startsWith( "MD5" ) )
+            {
+                int lastSpacePos = expectedChecksum.lastIndexOf( ' ' );
+                expectedChecksum = expectedChecksum.substring( lastSpacePos + 
1 );
+            }
+            else
+            {
+                // remove everything after the first space (if available)
+                int spacePos = expectedChecksum.indexOf( ' ' );
+
+                if ( spacePos != -1 )
+                {
+                    expectedChecksum = expectedChecksum.substring( 0, spacePos 
);
+                }
+            }
+            if ( expectedChecksum.equals( actualChecksum ) )
+            {
+                File checksumFile = new File( destination + 
checksumFileExtension );
+                if ( checksumFile.exists() ) checksumFile.delete();
+                FileUtils.copyFile( tempChecksumFile, checksumFile );
+            }
+            else
+            {
+                throw new ChecksumVerificationException( "Checksum failed on 
download: local = '" + actualChecksum +
+                    "'; remote = '" + expectedChecksum + "'" );
+            }
+        }
+        catch ( IOException e )
+        {
+            throw new ChecksumVerificationException( "Invalid checksum file", 
e );
+        }
+    }
 
 }
 


Reply via email to