implement snapshot policies: default is to only check once a day (after crossing midnight), or if --update-snapshots (-U) is specified on the command line

git-svn-id: https://svn.apache.org/repos/asf/maven/components/trunk@163711 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Brett Leslie Porter 2005-03-30 03:30:08 +00:00
parent b03435f9e1
commit eabafcc093
7 changed files with 187 additions and 66 deletions

View File

@ -52,6 +52,8 @@ public class SnapshotArtifactMetadata
private static final String UTC_TIMESTAMP_PATTERN = "yyyyMMdd.HHmmss";
private long lastModified = 0;
public SnapshotArtifactMetadata( Artifact artifact )
{
super( artifact, artifact.getArtifactId() + "-" + artifact.getBaseVersion() + "." + SNAPSHOT_VERSION_FILE );
@ -81,6 +83,7 @@ public class SnapshotArtifactMetadata
}
String path = getLocalRepositoryLocation( localRepository ).getPath();
FileUtils.fileWrite( path, constructVersion() );
lastModified = new File( path ).lastModified();
}
catch ( IOException e )
{
@ -101,7 +104,7 @@ public class SnapshotArtifactMetadata
public String constructVersion()
{
String version = artifact.getBaseVersion();
if ( timestamp != null )
if ( timestamp != null && buildNumber > 0 )
{
String newVersion = timestamp + "-" + buildNumber;
if ( version != null )
@ -157,15 +160,23 @@ public class SnapshotArtifactMetadata
return snapshotMetadata;
}
private void readFromFile( File destination )
private void readFromFile( File file )
throws IOException
{
String version = FileUtils.fileRead( destination );
String version = FileUtils.fileRead( file );
lastModified = file.lastModified();
if ( version.indexOf( "SNAPSHOT" ) >= 0 )
{
timestamp = null;
buildNumber = 0;
return;
}
int index = version.lastIndexOf( "-" );
timestamp = version.substring( 0, index );
buildNumber = Integer.valueOf( version.substring( index + 1 ) ).intValue();
index = version.indexOf( "-" );
index = timestamp.lastIndexOf( "-" );
if ( index >= 0 )
{
// ignore starting version part, will be prepended later
@ -196,6 +207,9 @@ public class SnapshotArtifactMetadata
{
SnapshotArtifactMetadata metadata = (SnapshotArtifactMetadata) o;
// TODO: probably shouldn't test timestamp - except that it may be used do differentiate for a build number of 0
// in the local repository. check, then remove from here and just compare the build numbers
if ( buildNumber > metadata.buildNumber )
{
return 1;
@ -209,4 +223,9 @@ public class SnapshotArtifactMetadata
return timestamp.compareTo( metadata.timestamp );
}
}
public long getLastModified()
{
return lastModified;
}
}

View File

@ -33,21 +33,46 @@ import org.apache.maven.wagon.repository.Repository;
public class ArtifactRepository
extends Repository
{
private final String snapshotPolicy;
private final ArtifactRepositoryLayout layout;
public static final String SNAPSHOT_POLICY_NEVER = "never";
public static final String SNAPSHOT_POLICY_ALWAYS = "always";
public static final String SNAPSHOT_POLICY_DAILY = "daily";
public static final String SNAPSHOT_POLICY_INTERVAL = "interval";
public ArtifactRepository( String id, String url, ArtifactRepositoryLayout layout )
{
this( id, url, layout, null );
}
public ArtifactRepository( String id, String url, AuthenticationInfo authenticationInfo,
ArtifactRepositoryLayout layout )
{
this( id, url, authenticationInfo, layout, null );
}
public ArtifactRepository( String id, String url, ArtifactRepositoryLayout layout, String snapshotPolicy )
{
super( id, url );
this.layout = layout;
this.snapshotPolicy = snapshotPolicy;
}
public ArtifactRepository( String id, String url, AuthenticationInfo authInfo, ArtifactRepositoryLayout layout )
public ArtifactRepository( String id, String url, AuthenticationInfo authInfo, ArtifactRepositoryLayout layout,
String snapshotPolicy )
{
super( id, url, authInfo );
this.layout = layout;
this.snapshotPolicy = snapshotPolicy;
}
public String pathOf( Artifact artifact )
@ -62,4 +87,8 @@ public class ArtifactRepository
return layout.pathOfMetadata( artifactMetadata );
}
public String getSnapshotPolicy()
{
return snapshotPolicy;
}
}

View File

@ -25,6 +25,8 @@ import org.apache.maven.artifact.repository.layout.ArtifactPathFormatException;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@ -50,9 +52,7 @@ public class SnapshotTransformation
public void transformForResolve( Artifact artifact, List remoteRepositories, ArtifactRepository localRepository )
throws ArtifactMetadataRetrievalException
{
// TODO: remove hack
if ( isSnapshot( artifact ) &&
!Boolean.valueOf( System.getProperty( "maven.debug.snapshot.disabled", "true" ) ).booleanValue() )
if ( isSnapshot( artifact ) )
{
// TODO: this mostly works, however...
// - we definitely need the manual/daily check as this is quite slow given the large number of snapshots inside m2 presently
@ -72,31 +72,65 @@ public class SnapshotTransformation
}
String version = localMetadata.constructVersion();
if ( !alreadyResolved( artifact ) )
// TODO: remove hack
if ( !alreadyResolved( artifact ) &&
!Boolean.valueOf( System.getProperty( "maven.debug.snapshot.disabled", "false" ) ).booleanValue() )
{
boolean foundRemote = false;
boolean checkedUpdates = false;
for ( Iterator i = remoteRepositories.iterator(); i.hasNext(); )
{
ArtifactRepository remoteRepository = (ArtifactRepository) i.next();
getLogger().info(
artifact.getArtifactId() + ": checking for updates from " + remoteRepository.getId() );
SnapshotArtifactMetadata remoteMetadata = SnapshotArtifactMetadata.retrieveFromRemoteRepository(
artifact, remoteRepository, wagonManager );
if ( remoteMetadata.compareTo( localMetadata ) > 0 )
String snapshotPolicy = remoteRepository.getSnapshotPolicy();
// TODO: should be able to calculate this less often
boolean checkForUpdates = false;
if ( ArtifactRepository.SNAPSHOT_POLICY_ALWAYS.equals( snapshotPolicy ) )
{
artifact.setRepository( remoteRepository );
checkForUpdates = true;
}
else if ( ArtifactRepository.SNAPSHOT_POLICY_DAILY.equals( snapshotPolicy ) )
{
// Note that if last modified is 0, it didn't exist, so this will be true
if ( getMidnightBoundary().after( new Date( localMetadata.getLastModified() ) ) )
{
checkForUpdates = true;
}
}
else if ( snapshotPolicy.startsWith( ArtifactRepository.SNAPSHOT_POLICY_INTERVAL ) )
{
String s = snapshotPolicy.substring( ArtifactRepository.SNAPSHOT_POLICY_INTERVAL.length() + 1 );
int minutes = Integer.valueOf( s ).intValue();
Calendar cal = Calendar.getInstance();
cal.add( Calendar.MINUTE, -minutes );
// Note that if last modified is 0, it didn't exist, so this will be true
if ( cal.getTime().after( new Date( localMetadata.getLastModified() ) ) )
{
checkForUpdates = true;
}
}
// else assume "never"
localMetadata = remoteMetadata;
foundRemote = true;
if ( checkForUpdates )
{
getLogger().info(
artifact.getArtifactId() + ": checking for updates from " + remoteRepository.getId() );
SnapshotArtifactMetadata remoteMetadata = SnapshotArtifactMetadata.retrieveFromRemoteRepository(
artifact, remoteRepository, wagonManager );
if ( remoteMetadata.compareTo( localMetadata ) > 0 )
{
artifact.setRepository( remoteRepository );
localMetadata = remoteMetadata;
}
checkedUpdates = true;
}
}
if ( foundRemote )
if ( checkedUpdates )
{
artifact.addMetadata( localMetadata );
localMetadata.storeInLocalRepository( localRepository );
}
if ( getLogger().isInfoEnabled() )
@ -104,7 +138,7 @@ public class SnapshotTransformation
if ( !version.equals( artifact.getBaseVersion() ) )
{
String message = artifact.getArtifactId() + ": resolved to version " + version;
if ( foundRemote )
if ( artifact.getRepository() != null )
{
message += " from repository " + artifact.getRepository().getId();
}
@ -122,6 +156,16 @@ public class SnapshotTransformation
}
}
private Date getMidnightBoundary()
{
Calendar cal = Calendar.getInstance();
cal.set( Calendar.HOUR_OF_DAY, 0 );
cal.set( Calendar.MINUTE, 0 );
cal.set( Calendar.SECOND, 0 );
cal.set( Calendar.MILLISECOND, 0 );
return cal.getTime();
}
private boolean alreadyResolved( Artifact artifact )
{
return resolvedArtifactCache.contains( getCacheKey( artifact ) );

View File

@ -31,4 +31,5 @@ public interface ArtifactRepositoryFactory
public ArtifactRepository createArtifactRepository( Repository modelRepository, MavenSettings settings,
ArtifactRepositoryLayout repositoryLayout );
void setGlobalSnapshotPolicy( String snapshotPolicy );
}

View File

@ -31,6 +31,7 @@ public class DefaultArtifactRepositoryFactory
extends AbstractLogEnabled
implements ArtifactRepositoryFactory
{
private String globalSnapshotPolicy = null;
public ArtifactRepository createArtifactRepository( Repository modelRepository, MavenSettings settings,
ArtifactRepositoryLayout repositoryLayout )
@ -55,6 +56,12 @@ public class DefaultArtifactRepositoryFactory
ArtifactRepository repo = null;
String snapshotPolicy = globalSnapshotPolicy;
if ( snapshotPolicy == null )
{
snapshotPolicy = modelRepository.getSnapshotPolicy();
}
if ( repoProfile != null )
{
AuthenticationInfo authInfo = new AuthenticationInfo();
@ -68,14 +75,20 @@ public class DefaultArtifactRepositoryFactory
authInfo.setPassphrase( repoProfile.getPassphrase() );
repo = new ArtifactRepository( modelRepository.getId(), modelRepository.getUrl(), authInfo,
repositoryLayout );
repositoryLayout, snapshotPolicy );
}
else
{
repo = new ArtifactRepository( modelRepository.getId(), modelRepository.getUrl(), repositoryLayout );
repo = new ArtifactRepository( modelRepository.getId(), modelRepository.getUrl(), repositoryLayout,
snapshotPolicy );
}
return repo;
}
public void setGlobalSnapshotPolicy( String snapshotPolicy )
{
this.globalSnapshotPolicy = snapshotPolicy;
}
}

View File

@ -67,7 +67,8 @@ public class MavenCli
public static File userDir = new File( System.getProperty( "user.dir" ) );
public static int main( String[] args, ClassWorld classWorld ) throws Exception
public static int main( String[] args, ClassWorld classWorld )
throws Exception
{
// ----------------------------------------------------------------------
// Setup the command line parser
@ -135,8 +136,13 @@ public class MavenCli
MavenSettings settings = settingsBuilder.buildSettings();
ArtifactRepositoryFactory artifactRepositoryFactory = (ArtifactRepositoryFactory) embedder
.lookup( ArtifactRepositoryFactory.ROLE );
ArtifactRepositoryFactory artifactRepositoryFactory = (ArtifactRepositoryFactory) embedder.lookup(
ArtifactRepositoryFactory.ROLE );
if ( commandLine.hasOption( CLIManager.UPDATE_SNAPSHOTS ) )
{
artifactRepositoryFactory.setGlobalSnapshotPolicy( ArtifactRepository.SNAPSHOT_POLICY_ALWAYS );
}
// TODO: Switch the default repository layout id to "default" when the
// conversion is done.
@ -152,10 +158,8 @@ public class MavenCli
repoLayoutId = "default";
}
ArtifactRepositoryLayout repositoryLayout = (ArtifactRepositoryLayout) embedder
.lookup(
ArtifactRepositoryLayout.ROLE,
repoLayoutId );
ArtifactRepositoryLayout repositoryLayout = (ArtifactRepositoryLayout) embedder.lookup(
ArtifactRepositoryLayout.ROLE, repoLayoutId );
ArtifactRepository localRepository = getLocalRepository( settings, artifactRepositoryFactory, repositoryLayout );
@ -302,43 +306,42 @@ public class MavenCli
// TODO: this is a hack until we can get the main repo converted...
public static final char VERSION_2_REPO = 'a';
public static final char UPDATE_SNAPSHOTS = 'U';
public CLIManager()
{
options = new Options();
options.addOption( OptionBuilder.withLongOpt( "nobanner" ).withDescription( "Suppress logo banner" )
.create( NO_BANNER ) );
options
.addOption( OptionBuilder.withLongOpt( "define" ).hasArg()
.withDescription( "Define a system property" ).create( SET_SYSTEM_PROPERTY ) );
options.addOption( OptionBuilder.withLongOpt( "offline" ).hasArg().withDescription( "Work offline" )
.create( WORK_OFFLINE ) );
options
.addOption( OptionBuilder.withLongOpt( "mojoDescriptors" )
.withDescription( "Display available mojoDescriptors" ).create( LIST_GOALS ) );
options.addOption( OptionBuilder.withLongOpt( "help" ).withDescription( "Display help information" )
.create( HELP ) );
options.addOption( OptionBuilder.withLongOpt( "offline" ).withDescription( "Build is happening offline" )
.create( WORK_OFFLINE ) );
options.addOption( OptionBuilder.withLongOpt( "version" ).withDescription( "Display version information" )
.create( VERSION ) );
options.addOption( OptionBuilder.withLongOpt( "debug" ).withDescription( "Produce execution debug output" )
.create( DEBUG ) );
options.addOption( OptionBuilder.withLongOpt( "reactor" )
.withDescription( "Execute goals for project found in the reactor" )
.create( REACTOR ) );
options.addOption( OptionBuilder.withLongOpt( "non-recursive" )
.withDescription( "Do not recurse into sub-projects" )
.create( NON_RECURSIVE ) );
options.addOption( OptionBuilder.withLongOpt( "v1-local-repository" )
.withDescription( "Use legacy layout for local artifact repository" )
.create( VERSION_1_REPO ) );
options.addOption( OptionBuilder.withLongOpt( "nobanner" ).withDescription( "Suppress logo banner" ).create(
NO_BANNER ) );
options.addOption( OptionBuilder.withLongOpt( "define" ).hasArg().withDescription(
"Define a system property" ).create( SET_SYSTEM_PROPERTY ) );
options.addOption( OptionBuilder.withLongOpt( "offline" ).hasArg().withDescription( "Work offline" ).create(
WORK_OFFLINE ) );
options.addOption( OptionBuilder.withLongOpt( "mojoDescriptors" ).withDescription(
"Display available mojoDescriptors" ).create( LIST_GOALS ) );
options.addOption( OptionBuilder.withLongOpt( "help" ).withDescription( "Display help information" ).create(
HELP ) );
options.addOption( OptionBuilder.withLongOpt( "offline" ).withDescription( "Build is happening offline" ).create(
WORK_OFFLINE ) );
options.addOption( OptionBuilder.withLongOpt( "version" ).withDescription( "Display version information" ).create(
VERSION ) );
options.addOption( OptionBuilder.withLongOpt( "debug" ).withDescription( "Produce execution debug output" ).create(
DEBUG ) );
options.addOption( OptionBuilder.withLongOpt( "reactor" ).withDescription(
"Execute goals for project found in the reactor" ).create( REACTOR ) );
options.addOption( OptionBuilder.withLongOpt( "non-recursive" ).withDescription(
"Do not recurse into sub-projects" ).create( NON_RECURSIVE ) );
options.addOption( OptionBuilder.withLongOpt( "v1-local-repository" ).withDescription(
"Use legacy layout for local artifact repository" ).create( VERSION_1_REPO ) );
options.addOption( OptionBuilder.withLongOpt( "v2-local-repository" )
.withDescription( "Use new layout for local artifact repository" )
.create( VERSION_2_REPO ) );
options.addOption( OptionBuilder.withLongOpt( "v2-local-repository" ).withDescription(
"Use new layout for local artifact repository" ).create( VERSION_2_REPO ) );
options.addOption( OptionBuilder.withLongOpt( "update-snapshots" ).withDescription(
"Update all snapshots regardless of repository policies" ).create( UPDATE_SNAPSHOTS ) );
}
public CommandLine parse( String[] args ) throws ParseException
public CommandLine parse( String[] args )
throws ParseException
{
CommandLineParser parser = new PosixParser();
return parser.parse( options, args );
@ -384,8 +387,9 @@ public class MavenCli
}
protected static ArtifactRepository getLocalRepository( MavenSettings settings,
ArtifactRepositoryFactory repoFactory,
ArtifactRepositoryLayout repositoryLayout ) throws Exception
ArtifactRepositoryFactory repoFactory,
ArtifactRepositoryLayout repositoryLayout )
throws Exception
{
Profile profile = settings.getActiveProfile();
@ -398,7 +402,8 @@ public class MavenCli
if ( localRepository == null )
{
String userConfigurationDirectory = System.getProperty( "user.home" ) + "/.m2";
localRepository = new File( userConfigurationDirectory, MavenConstants.MAVEN_REPOSITORY ).getAbsolutePath();
localRepository =
new File( userConfigurationDirectory, MavenConstants.MAVEN_REPOSITORY ).getAbsolutePath();
}
// TODO [BP]: this should not be necessary - grep for and remove

View File

@ -1850,6 +1850,16 @@
]]></description>
<type>String</type>
</field>
<field>
<name>snapshotPolicy</name>
<version>4.0.0</version>
<description>
The policy for downloading snapshots - can be "always", "daily" (default), "interval:XXX" (in minutes) or
"never".
</description>
<type>String</type>
<defaultValue>daily</defaultValue>
</field>
</fields>
<codeSegments>
<codeSegment>