package org.apache.maven.verifier; /* ==================================================================== * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ==================================================================== */ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.maven.AbstractMavenComponent; import org.apache.maven.project.Project; import org.apache.maven.repository.Artifact; import org.apache.maven.util.HttpUtils; import org.apache.maven.MavenConstants; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Make sure that everything that is required for the project to build * successfully is present. We will start by looking at the dependencies * and make sure they are all here before trying to compile. * * @author Jason van Zyl * @author Vincent Massol * * @version $Id: DependencyVerifier.java,v 1.34.4.7 2004/06/27 07:57:48 brett Exp $ * * @todo Separate out the local settings verifier because this only needs to be run * once a session, but is currently being run during project verification so * this is a big waste in the reactor for example. * @todo For a given maven session keep track of what snapshots have been downloaded * so that we can prevent the same snapshots retrieval attempts from happening * repeatedly. I think we can assume in a single session the snapshot won't * change. This would certainly help is large reactor builds like the jelly * tags where all of them depend on a jelly snapshot. */ public class DependencyVerifier extends AbstractMavenComponent { /** log for debug output */ private static final Log log = LogFactory.getLog(DependencyVerifier.class); /** Control the use of timestamp comparison when downloading missing resources. */ private boolean useTimestamp = true; /** Control continuation upon errors during the downloading of missing resources. */ private boolean ignoreErrors = true; /** List of failed deps. */ private List failedDependencies; /** Local Repository verifier. */ private LocalSettingsVerifier localRepositoryVerifier; /** * Default ctor. * @param project the project to verify */ public DependencyVerifier( Project project ) { super( project ); failedDependencies = new ArrayList(); localRepositoryVerifier = new LocalSettingsVerifier( project ); } /** * Execute the verification process. * * @throws RepoConfigException If an error occurs while verifying basic maven settings. * @throws UnsatisfiedDependencyException If there are unsatisfied dependencies. * @throws ChecksumVerificationException if the download checksum doesn't match the calculated */ public void verify() throws RepoConfigException, UnsatisfiedDependencyException, ChecksumVerificationException { localRepositoryVerifier.verifyLocalRepository(); satisfyDependencies(); //verifyDependencies(); } /** * Clear the failed dependencies. Required when reusing the * project verifier. */ private void clearFailedDependencies() { for ( int i = 0; i < failedDependencies.size(); i++ ) { failedDependencies.remove( i ); } } /** * Check to see that all dependencies are present and if they are * not then download them. * * @throws UnsatisfiedDependencyException If there are unsatisfied dependencies. */ private void satisfyDependencies() throws UnsatisfiedDependencyException { // Is the remote repository enabled? boolean remoteRepoEnabled = getProject().getContext().getRemoteRepositoryEnabled().booleanValue(); // Is the user online? boolean online = getProject().getContext().getOnline().booleanValue(); if ( !remoteRepoEnabled ) { log.warn( getMessage( "remote.repository.disabled.warning" ) ); } clearFailedDependencies(); for ( Iterator i = getProject().getArtifacts().iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); // The artifact plain doesn't exist so chalk it up as a failed dependency. if ( !artifact.exists() ) { failedDependencies.add( artifact ); } else { // The artifact exists but we need to take into account the user // being online and whether the artifact is a snapshot. If the user // is online then snapshots are added to the list of failed dependencies // so that a newer version can be retrieved if one exists. We make // an exception when the user is working offline and let them // take their chances with a strong warning that they could possibly // be using an out-of-date artifact. We don't want to cripple users // when working offline. if ( online && artifact.isSnapshot() ) { failedDependencies.add( artifact ); } else if ( !online && artifact.isSnapshot() ) { log.warn( getMessage( "offline.snapshot.warning", artifact.getName() ) ); } } } // If we have any failed dependencies then we will attempt to download // them for the user if the remote repository is enabled. if ( !failedDependencies.isEmpty() && remoteRepoEnabled && online ) { getDependencies(); } // If we still have failed dependencies after we have tried to // satisfy all dependencies then we have a problem. There might // also be a problem if the use of the remote repository has // been disabled and dependencies just aren't present. In any // case we have a problem. if ( !failedDependencies.isEmpty() ) { throw new UnsatisfiedDependencyException( createUnsatisfiedDependenciesMessage() ); } } /** * Create a message for the user stating the dependencies that are unsatisfied. * * @return The unsatisfied dependency message. */ private String createUnsatisfiedDependenciesMessage() { StringBuffer message = new StringBuffer(); if ( failedDependencies.size() == 1 ) { message.append( getMessage( "single.unsatisfied.dependency.error" ) ); } else { message.append( getMessage( "multiple.unsatisfied.dependency.error" ) ); } message.append( "\n\n" ); for ( Iterator i = failedDependencies.iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); message.append( artifact.getName() ); String url = artifact.getDependency().getUrl(); if (StringUtils.isNotEmpty(url)) { // FIXME: internationalize message.append( " (") .append("try downloading from ") .append( url ) .append( ")"); } message.append( "\n" ); } return message.toString(); } /** * Rules for verifying the checksum. * * We attempt to download artifacts and their accompanying md5 checksum * files. */ /** * Try and retrieve the dependencies from the remote repository in * order to satisfy the dependencies of the project. */ private void getDependencies() { for ( Iterator i = failedDependencies.iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); // The directory structure for the project this dependency belongs to // may not exists so attempt to create the project directory structure // before attempting to download the dependency. File directory = artifact.getFile().getParentFile(); if ( !directory.exists() ) { directory.mkdirs(); } log.info( getMessage( "download.message", artifact.getName() ) ); if ( getRemoteArtifact( artifact ) ) { // The dependency has been successfully downloaded so lets remove // it from the failed dependency list. i.remove(); } else { if (artifact.exists()) { // The snapshot jar locally exists and not in remote repository // FIXME: localize this message log.debug("Artifact " + artifact.getUrlPath() + " doesn't exists in remote repository, but it exists locally"); i.remove(); } else { String warning = getMessage( "failed.download.warning", artifact.getName() ); log.warn( warning ); } } } } /** * Retrieve a remoteFile from the maven remote repositories * and store it at localFile * @param artifact the artifact to retrieve from the repositories. * @return true if the retrieval succeeds, false otherwise. */ private boolean getRemoteArtifact( Artifact artifact ) { boolean artifactFound = false; try { HttpUtils.getFile( artifact.getUrlPath(), artifact.getFile(), ignoreErrors, useTimestamp, true , getProject().getContext() ); artifactFound = true; } catch ( IOException e ) { log.warn("Error retrieving artifact from [" + artifact.getUrlPath() + "]: " + e); log.debug("Error details", e); } return artifactFound; } // ---------------------------------------------------------------------- // V E R I F I C A T I O N // ---------------------------------------------------------------------- /** * Verify each of the artifacts if an md5 checksum is available. * XXX As this is not used, it has probably never been tested. * @throws ChecksumVerificationException If we encounter an artifact that * is corrupted. */ private void verifyDependencies() throws ChecksumVerificationException { for ( Iterator i = getProject().getArtifacts().iterator(); i.hasNext();) { Artifact artifact = (Artifact) i.next(); artifact.verify(); } } /** * @return true if the ignore errors flag is set. */ public boolean isIgnoreErrors() { return ignoreErrors; } /** * Sets the ignoreErrors flag. * @param ignoreErrors The ignoreErrors to set */ public void setIgnoreErrors(boolean ignoreErrors) { this.ignoreErrors = ignoreErrors; } }