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;
}
}