From 46e8e5524f6ff1713ae3052bb745c672accc79d6 Mon Sep 17 00:00:00 2001 From: Richard Atkins Date: Mon, 15 Apr 2019 11:21:23 +1000 Subject: [PATCH 1/2] Add support for mocking timetstamped snapshots * Ensure any poms loaded from the mrm repo directory that have timestamps for their versions are registered as snapshots, so that we can easily load them later * Add factory methods to Artifact to make it easier to build new Artifacts from an existing Artifact object definition * Make Artifact responsible for the parsing logic of timestamped versions, to avoid copying it into MockArtifactStore to use during its registration phase * Fix typos in FileSystemArtifactStore and ArtifactStoreFileSystem javadoc --- .../codehaus/mojo/mrm/api/maven/Artifact.java | 61 +++++++++- .../impl/maven/ArtifactStoreFileSystem.java | 36 ++++-- .../impl/maven/FileSystemArtifactStore.java | 2 +- .../mrm/impl/maven/MockArtifactStore.java | 113 ++++++++++-------- 4 files changed, 144 insertions(+), 68 deletions(-) diff --git a/mrm-api/src/main/java/org/codehaus/mojo/mrm/api/maven/Artifact.java b/mrm-api/src/main/java/org/codehaus/mojo/mrm/api/maven/Artifact.java index 54d20a84..dd8ba47d 100644 --- a/mrm-api/src/main/java/org/codehaus/mojo/mrm/api/maven/Artifact.java +++ b/mrm-api/src/main/java/org/codehaus/mojo/mrm/api/maven/Artifact.java @@ -19,8 +19,12 @@ import java.io.Serializable; import java.text.MessageFormat; import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; +import java.util.GregorianCalendar; import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Represents a specific artifact in a Maven repository. Implements {@link Comparable} to sort based on @@ -32,6 +36,9 @@ public final class Artifact implements Serializable, Comparable { + static final String SNAPSHOT_TIMESTAMP_VERSION_REGEX = + "([^/]+)-(\\d{4})(\\d{2})(\\d{2})\\.(\\d{2})(\\d{2})(\\d{2})-(\\d+)"; + static final Pattern SNAPSHOT_TIMESTAMP_VERSION = Pattern.compile( SNAPSHOT_TIMESTAMP_VERSION_REGEX ); /** * Ensure consistent serialization. @@ -202,6 +209,55 @@ public Artifact( String groupId, String artifactId, String version, String type this( groupId, artifactId, version, null, type ); } + public static Artifact createSimpleArtifact( String groupId, String artifactId, String version, String type ) + { + return new Artifact( groupId, artifactId, version, type ); + } + + public Artifact withType( String type ) + { + return new Artifact( this.groupId, this.artifactId, this.version, this.classifier, type, this.timestamp, + this.buildNumber ); + } + + public Artifact withTimestampAndBuildNumber( long timestamp, int buildNumber ) + { + return new Artifact( this.groupId, this.artifactId, this.version, this.classifier, this.type, timestamp, + buildNumber ); + } + + public Artifact withClassifier( String classifier ) + { + return new Artifact( this.groupId, this.artifactId, this.version, classifier, this.type, this.timestamp, + this.buildNumber ); + } + + public Artifact withParsedVersion(String versionString ) + { + Matcher matcher = SNAPSHOT_TIMESTAMP_VERSION.matcher( versionString ); + if (matcher.matches()) + { + String version = matcher.group(1) + "-SNAPSHOT"; + Calendar cal = new GregorianCalendar(); + cal.setTimeZone( TimeZone.getTimeZone( "GMT" ) ); + cal.set( Calendar.YEAR, Integer.parseInt( matcher.group( 2 ) ) ); + cal.set( Calendar.MONTH, Integer.parseInt( matcher.group( 3 ) ) - 1 ); + cal.set( Calendar.DAY_OF_MONTH, Integer.parseInt( matcher.group( 4 ) ) ); + cal.set( Calendar.HOUR_OF_DAY, Integer.parseInt( matcher.group( 5 ) ) ); + cal.set( Calendar.MINUTE, Integer.parseInt( matcher.group( 6 ) ) ); + cal.set( Calendar.SECOND, Integer.parseInt( matcher.group( 7 ) ) ); + cal.set( Calendar.MILLISECOND, 0); + + long timestamp = cal.getTimeInMillis(); + int buildNumber = Integer.parseInt( matcher.group( 8 ) ); + + return new Artifact( this.groupId, this.artifactId, version, this.classifier, this.type, timestamp, + buildNumber); + } + return new Artifact( this.groupId, this.artifactId, versionString, this.classifier, this.type, this.timestamp, + this.buildNumber ); + } + /** * Returns the name of the artifact. * @@ -336,11 +392,10 @@ public String getTimestampVersion() if ( timestamp != null ) { assert isSnapshot(); - SimpleDateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" ); - fmt.setTimeZone( TimeZone.getTimeZone( "GMT" ) ); + String timestampString = getTimestampString(); timestampVersion = MessageFormat.format( "{0}-{1}-{2}", new Object[]{ this.version.substring( 0, this.version.length() - "-SNAPSHOT".length() ), - fmt.format( new Date( timestamp.longValue() ) ), buildNumber } ); + timestampString, String.valueOf( buildNumber ) } ); } else { diff --git a/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/ArtifactStoreFileSystem.java b/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/ArtifactStoreFileSystem.java index 6fab8d76..604241e8 100644 --- a/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/ArtifactStoreFileSystem.java +++ b/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/ArtifactStoreFileSystem.java @@ -48,7 +48,7 @@ /** * A {@link org.codehaus.mojo.mrm.api.FileSystem} that delegates to a {@link ArtifactStore}. * - * @see FileSystemArtifactStore for the oposite. + * @see FileSystemArtifactStore for the opposite. * @since 1.0 */ public class ArtifactStoreFileSystem @@ -335,9 +335,12 @@ else if ( ARCHETYPE_CATALOG.matcher( path ).matches() ) cal.set( Calendar.MILLISECOND, 0); long timestamp = cal.getTimeInMillis(); int buildNumber = Integer.parseInt( matcher.group( 8 ) ); - - Artifact artifact = new Artifact( groupId, artifactId, version, matcher.group( 9 ), - matcher.group( 10 ), timestamp, buildNumber ); + + String type = matcher.group(10); + String classifier = matcher.group(9); + Artifact artifact = Artifact.createSimpleArtifact( groupId, artifactId, version, type ) + .withClassifier( classifier ) + .withTimestampAndBuildNumber( timestamp, buildNumber ); try { store.get( artifact ); @@ -483,12 +486,15 @@ private Artifact getArtifact( DirectoryEntry parent, String name ) { classifier = null; } - return new Artifact( groupId, artifactId, version, classifier, type ); + return Artifact.createSimpleArtifact( groupId, artifactId, version, type ) + .withClassifier( classifier ); } if ( matcher.group( 1 ).equals( "SNAPSHOT" ) ) { - return new Artifact( groupId, artifactId, version, matcher.group( 9 ), - matcher.group( 10 ) ); + String classifier = matcher.group(9); + String type = matcher.group(10); + return Artifact.createSimpleArtifact( groupId, artifactId, version, type ) + .withClassifier( classifier ); } try { @@ -503,9 +509,12 @@ private Artifact getArtifact( DirectoryEntry parent, String name ) cal.set( Calendar.MILLISECOND, 0); long timestamp = cal.getTimeInMillis(); int buildNumber = Integer.parseInt( matcher.group( 8 ) ); - - return new Artifact( groupId, artifactId, version, matcher.group( 9 ), - matcher.group( 10 ), timestamp, buildNumber ); + + String classifier = matcher.group(9); + String type = matcher.group(10); + return Artifact.createSimpleArtifact( groupId, artifactId, version, type ) + .withClassifier( classifier ) + .withTimestampAndBuildNumber( timestamp, buildNumber ); } catch ( NullPointerException e ) { @@ -522,16 +531,17 @@ private Artifact getArtifact( DirectoryEntry parent, String name ) String version = matcher.group( 3 ); String classifier = matcher.group( 5 ); String type = matcher.group( 6 ); + Artifact artifact = Artifact.createSimpleArtifact(groupId, artifactId, version, type); if ( classifier != null ) { classifier = classifier.substring( 1 ); } - if ( StringUtils.isEmpty( classifier ) ) + if ( StringUtils.isNotEmpty( classifier ) ) { - classifier = null; + artifact = artifact.withClassifier(classifier); } - return new Artifact( groupId, artifactId, version, classifier, type ); + return artifact; } else { diff --git a/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/FileSystemArtifactStore.java b/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/FileSystemArtifactStore.java index 881ffc51..a95fb916 100644 --- a/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/FileSystemArtifactStore.java +++ b/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/FileSystemArtifactStore.java @@ -47,7 +47,7 @@ /** * An artifact store based off a {@link FileSystem}. * - * @see ArtifactStoreFileSystem for the oposite. + * @see ArtifactStoreFileSystem for the opposite. * @since 1.0 */ public class FileSystemArtifactStore diff --git a/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/MockArtifactStore.java b/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/MockArtifactStore.java index e6060128..11a655ff 100644 --- a/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/MockArtifactStore.java +++ b/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/MockArtifactStore.java @@ -75,7 +75,6 @@ public class MockArtifactStore extends BaseArtifactStore { - private final Log log; private final boolean lazyArchiver; @@ -135,8 +134,8 @@ public MockArtifactStore( Log log, File root, boolean lazyArchiver ) if ( root.isDirectory() ) { MavenXpp3Reader pomReader = new MavenXpp3Reader(); - Collection poms = FileUtils.listFiles( root, POM_EXTENSIONS, true ); - for ( File file : poms ) + Collection pomFiles = FileUtils.listFiles( root, POM_EXTENSIONS, true ); + for ( File file : pomFiles ) { FileReader fileReader; try @@ -144,62 +143,74 @@ public MockArtifactStore( Log log, File root, boolean lazyArchiver ) fileReader = new FileReader( file ); Model model = pomReader.read( fileReader ); String groupId = model.getGroupId() != null ? model.getGroupId() : model.getParent().getGroupId(); - String version = model.getVersion() != null ? model.getVersion() : model.getParent().getVersion(); - set( new Artifact( groupId, model.getArtifactId(), version, "pom" ), - new FileContent( file ) ); - - final String basename = FilenameUtils.getBaseName( file.getName() ); - - if ( StringUtils.isEmpty( model.getPackaging() ) || "jar".equals( model.getPackaging() ) ) - { - File mainFile = new File( file.getParentFile(), basename + ".jar" ); - - Content content; - if( mainFile.isDirectory() ) - { - content = new DirectoryContent( mainFile, lazyArchiver ); - } - else - { - content = new BytesContent( Utils.newEmptyJarContent() ); - } - - set( new Artifact( groupId, model.getArtifactId(), version, "jar" ), content ); - } - else if ( "maven-plugin".equals( model.getPackaging() ) ) + String baseVersion = model.getVersion() != null ? model.getVersion() : model.getParent().getVersion(); + // register a version (like 1.2.3 or 1.0-SNAPSHOT) and a timestamp (if any) + Artifact baseArtifact = Artifact.createSimpleArtifact( groupId, model.getArtifactId(), baseVersion, "pom" ); + List poms = new ArrayList<>(); + Artifact maybeTimestampedPom = baseArtifact.withParsedVersion( baseVersion ); + poms.add( maybeTimestampedPom ); + if ( maybeTimestampedPom.getTimestamp() != null ) { - set( new Artifact( groupId, model.getArtifactId(), version, "jar" ), - new BytesContent( - Utils.newEmptyMavenPluginJarContent( groupId, model.getArtifactId(), - version ) ) ); + // also register a version without a timestamp and build number + Artifact snapshotPom = Artifact.createSimpleArtifact( + maybeTimestampedPom.getGroupId(), + maybeTimestampedPom.getArtifactId(), + maybeTimestampedPom.getVersion(), + maybeTimestampedPom.getType()); + poms.add(snapshotPom); } - - Collection classifiedFiles = Arrays.asList( file.getParentFile().listFiles( new FilenameFilter() + for ( Artifact pom : poms ) { - @Override - public boolean accept( File dir, String name ) + String version = pom.getVersion(); + set( pom, new FileContent( file ) ); + + final String basename = FilenameUtils.getBaseName( file.getName() ); + + if (StringUtils.isEmpty( model.getPackaging()) || "jar".equals( model.getPackaging() ) ) { - return FilenameUtils.getBaseName( name ).startsWith( basename + '-' ); + File mainFile = new File( file.getParentFile(), basename + ".jar" ); + + Content content; + if ( mainFile.isDirectory() ) + { + content = new DirectoryContent( mainFile, lazyArchiver ); + } + else + { + content = new BytesContent( Utils.newEmptyJarContent() ); + } + + set( pom.withType("jar"), content ); } - } ) ); - - for ( File classifiedFile : classifiedFiles ) - { - String type = org.codehaus.plexus.util.FileUtils.extension( classifiedFile.getName() ); - String classifier = - FilenameUtils.getBaseName( classifiedFile.getName() ).substring( basename.length() + 1 ); - - Content content; - if( classifiedFile.isDirectory() ) + else if ("maven-plugin".equals(model.getPackaging())) { - content = new DirectoryContent( classifiedFile, lazyArchiver ); + set( pom.withType("jar"), + new BytesContent( + Utils.newEmptyMavenPluginJarContent(groupId, model.getArtifactId(), + version ) ) ); } - else - { - content = new FileContent( classifiedFile ); + + Collection classifiedFiles = Arrays.asList(file.getParentFile().listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return FilenameUtils.getBaseName(name).startsWith(basename + '-'); + } + })); + + for (File classifiedFile : classifiedFiles) { + String type = org.codehaus.plexus.util.FileUtils.extension(classifiedFile.getName()); + String classifier = + FilenameUtils.getBaseName(classifiedFile.getName()).substring(basename.length() + 1); + + Content content; + if (classifiedFile.isDirectory()) { + content = new DirectoryContent(classifiedFile, lazyArchiver); + } else { + content = new FileContent(classifiedFile); + } + + set(pom.withType(type).withClassifier(classifier), content); } - - set( new Artifact( groupId, model.getArtifactId(), version, classifier, type ), content ); } } catch ( IOException e ) From 846b0b4f95d2ef3888aafb8e6a4ee05ec2f7f296 Mon Sep 17 00:00:00 2001 From: Richard Atkins Date: Mon, 15 Apr 2019 13:46:48 +1000 Subject: [PATCH 2/2] Improve support for mocking timestampted snapshots * Collect the timestamp and build number directly from the filename on disk, rather than relying on these to be embedded in the artifact version element. This matches standard maven repo behaviour. --- .../mrm/impl/maven/MockArtifactStore.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/MockArtifactStore.java b/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/MockArtifactStore.java index 11a655ff..5dccd559 100644 --- a/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/MockArtifactStore.java +++ b/mrm-servlet/src/main/java/org/codehaus/mojo/mrm/impl/maven/MockArtifactStore.java @@ -37,6 +37,8 @@ import java.util.Set; import java.util.TimeZone; import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; @@ -79,6 +81,8 @@ public class MockArtifactStore private final boolean lazyArchiver; + private String SNAPSHOT_VERSION_REGEX = "([^/]+-(SNAPSHOT|\\d{8}\\.\\d{6}-\\d+))"; + /** * The extensions to search for when looking for POMs to mock. * @@ -140,24 +144,27 @@ public MockArtifactStore( Log log, File root, boolean lazyArchiver ) FileReader fileReader; try { + String filename = file.getName(); fileReader = new FileReader( file ); Model model = pomReader.read( fileReader ); String groupId = model.getGroupId() != null ? model.getGroupId() : model.getParent().getGroupId(); + String artifactId = model.getArtifactId(); String baseVersion = model.getVersion() != null ? model.getVersion() : model.getParent().getVersion(); - // register a version (like 1.2.3 or 1.0-SNAPSHOT) and a timestamp (if any) - Artifact baseArtifact = Artifact.createSimpleArtifact( groupId, model.getArtifactId(), baseVersion, "pom" ); + // register a version (like 1.2.3 or 1.0-SNAPSHOT) + Artifact baseArtifact = Artifact.createSimpleArtifact( groupId, artifactId, baseVersion, "pom" ); List poms = new ArrayList<>(); - Artifact maybeTimestampedPom = baseArtifact.withParsedVersion( baseVersion ); - poms.add( maybeTimestampedPom ); - if ( maybeTimestampedPom.getTimestamp() != null ) + poms.add( baseArtifact ); + if (baseVersion.endsWith("-SNAPSHOT")) { - // also register a version without a timestamp and build number - Artifact snapshotPom = Artifact.createSimpleArtifact( - maybeTimestampedPom.getGroupId(), - maybeTimestampedPom.getArtifactId(), - maybeTimestampedPom.getVersion(), - maybeTimestampedPom.getType()); - poms.add(snapshotPom); + // load the timestamp from the filename, if any + Pattern fileVersionPattern = Pattern.compile(artifactId + "-" + SNAPSHOT_VERSION_REGEX + "\\.pom"); + Matcher fileVersionMatcher = fileVersionPattern.matcher(filename); + if (fileVersionMatcher.matches() && !fileVersionMatcher.group(2).equals("SNAPSHOT")) + { + String timestampVersion = fileVersionMatcher.group(1); + Artifact snapshotPom = baseArtifact.withParsedVersion(timestampVersion); + poms.add(snapshotPom); + } } for ( Artifact pom : poms ) { @@ -186,7 +193,7 @@ else if ("maven-plugin".equals(model.getPackaging())) { set( pom.withType("jar"), new BytesContent( - Utils.newEmptyMavenPluginJarContent(groupId, model.getArtifactId(), + Utils.newEmptyMavenPluginJarContent(groupId, artifactId, version ) ) ); }