diff --git a/maven-repository-provisioner/src/main/java/com/simpligility/maven/Gav.java b/maven-repository-provisioner/src/main/java/com/simpligility/maven/Gav.java index 968c28e..6449335 100644 --- a/maven-repository-provisioner/src/main/java/com/simpligility/maven/Gav.java +++ b/maven-repository-provisioner/src/main/java/com/simpligility/maven/Gav.java @@ -4,6 +4,8 @@ */ package com.simpligility.maven; +import java.util.Objects; + public final class Gav { private final String groupId; @@ -60,6 +62,26 @@ public String getRepositoryURLPath() { return groupId.replace(".", "/") + "/" + artifactId + "/" + version + "/"; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Gav gav = (Gav) obj; + return groupId.equals(gav.getGroupId()) + && artifactId.equals(gav.getArtifactId()) + && version.equals(gav.getVersion()) + && packaging.equals(gav.getPackaging()); + } + + @Override + public int hashCode() { + return Objects.hash(groupId, artifactId, version, packaging); + } + @Override public String toString() { return groupId + ":" + artifactId + ":" + version; diff --git a/maven-repository-provisioner/src/main/java/com/simpligility/maven/GavMatcher.java b/maven-repository-provisioner/src/main/java/com/simpligility/maven/GavMatcher.java new file mode 100644 index 0000000..6591c8b --- /dev/null +++ b/maven-repository-provisioner/src/main/java/com/simpligility/maven/GavMatcher.java @@ -0,0 +1,24 @@ +package com.simpligility.maven; + +import java.util.concurrent.Callable; +import java.util.function.Supplier; + +public class GavMatcher implements Callable, Supplier { + private final Gav gav; + private final GavPattern pattern; + + public GavMatcher(Gav gav, GavPattern pattern) { + this.gav = gav; + this.pattern = pattern; + } + + @Override + public Boolean call() { + return pattern.matches(gav); + } + + @Override + public Boolean get() { + return call(); + } +} diff --git a/maven-repository-provisioner/src/main/java/com/simpligility/maven/GavPattern.java b/maven-repository-provisioner/src/main/java/com/simpligility/maven/GavPattern.java new file mode 100644 index 0000000..b0c5cda --- /dev/null +++ b/maven-repository-provisioner/src/main/java/com/simpligility/maven/GavPattern.java @@ -0,0 +1,20 @@ +package com.simpligility.maven; + +import java.util.regex.Pattern; + +public class GavPattern { + private final Pattern pattern; + + public GavPattern(Pattern pattern) { + this.pattern = pattern; + } + + public boolean matches(Gav gav) { + if (gav == null) { + return false; + } + String gavString = + gav.getGroupId() + ":" + gav.getArtifactId() + ":" + gav.getVersion() + ":" + gav.getPackaging(); + return pattern.matcher(gavString).matches(); + } +} diff --git a/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/Configuration.java b/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/Configuration.java index c6b17ca..966d9ee 100644 --- a/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/Configuration.java +++ b/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/Configuration.java @@ -109,6 +109,13 @@ public class Configuration { arity = 1) private Boolean verifyOnly = false; + @Parameter( + names = {"-df", "deployFilterFile"}, + description = "File containing a list of GAVs to deploy, one per line in format of groupId:artifactId:" + + "version:extension", + arity = 1) + private String deployFilterFile; + public void setSourceUrl(String sourceUrl) { this.sourceUrl = sourceUrl; } @@ -165,6 +172,10 @@ public void setVerifyOnly(Boolean verifyOnly) { this.verifyOnly = verifyOnly; } + public void setDeployFilterFile(String deployFilterFile) { + this.deployFilterFile = deployFilterFile; + } + public boolean getHelp() { return help; } @@ -241,6 +252,10 @@ public Boolean getVerifyOnly() { return verifyOnly; } + public String getDeployFilterFile() { + return deployFilterFile; + } + public List getArtifactCoordinates() { List coords = Arrays.asList(artifactCoordinate.split("\\|")); return coords; diff --git a/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/GavMatcherExecutor.java b/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/GavMatcherExecutor.java new file mode 100644 index 0000000..0e7b332 --- /dev/null +++ b/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/GavMatcherExecutor.java @@ -0,0 +1,35 @@ +package com.simpligility.maven.provisioner; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import com.simpligility.maven.Gav; +import com.simpligility.maven.GavMatcher; +import com.simpligility.maven.GavPattern; + +public class GavMatcherExecutor implements AutoCloseable { + private final ExecutorService executorService; + + public GavMatcherExecutor(int threadPoolSize) { + this.executorService = Executors.newFixedThreadPool(threadPoolSize); + } + + public List> evaluateGav(Gav gav, Set patterns) { + List> futures = new ArrayList<>(); + for (GavPattern pattern : patterns) { + CompletableFuture future = + CompletableFuture.supplyAsync(new GavMatcher(gav, pattern), executorService); + futures.add(future); + } + return futures; + } + + @Override + public void close() throws Exception { + executorService.shutdown(); + } +} diff --git a/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/MavenRepositoryDeployer.java b/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/MavenRepositoryDeployer.java index 61f913e..6ac09a1 100644 --- a/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/MavenRepositoryDeployer.java +++ b/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/MavenRepositoryDeployer.java @@ -11,9 +11,16 @@ import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.regex.Pattern; import com.simpligility.maven.Gav; +import com.simpligility.maven.GavPattern; import com.simpligility.maven.GavUtil; import com.simpligility.maven.MavenConstants; import org.apache.commons.io.FileUtils; @@ -59,9 +66,19 @@ public class MavenRepositoryDeployer { private final TreeSet potentialDeploys = new TreeSet(); - public MavenRepositoryDeployer(File repositoryPath) { + private final Set gavPatterns; + + public MavenRepositoryDeployer(File repositoryPath, String deployFilterFile) { this.repositoryPath = repositoryPath; initialize(); + gavPatterns = loadGavPatternsFromFilterFile(deployFilterFile); + } + + /** + * Default constructor to make unit testing easier + */ + public MavenRepositoryDeployer() { + gavPatterns = new HashSet<>(); } private void initialize() { @@ -123,6 +140,12 @@ public void deployToRemote( Gav gav = GavUtil.getGavFromRepositoryPath(leafRepoPath); + if (!canDeployGav(gav, 10)) { + logger.info("Skipping deployment of " + gav + " as it is not in the deploy filter file."); + skippedDeploys.add(gav.toString()); + continue; + } + boolean pomInTarget = false; if (checkTarget) { pomInTarget = checkIfPomInTarget(targetUrl, username, password, gav); @@ -209,6 +232,14 @@ public void deployToRemote( } } } + summarize(); + } + + public void summarize() { + logger.info("Deployed {} artifacts.", successfulDeploys.size()); + logger.info("Failed to deploy {} artifacts.", failedDeploys.size()); + logger.info("Skipped {} artifacts.", skippedDeploys.size()); + logger.info("Potentially deploy {} artifacts.", potentialDeploys.size()); } /** @@ -322,4 +353,53 @@ public boolean hasFailure() { public String getFailureMessage() { return "Failed to deploy some artifacts."; } + + public Set loadGavPatternsFromFilterFile(String deployFilterFile) { + Set gavPatterns = new HashSet<>(); + if (deployFilterFile != null) { + try { + File file = new File(deployFilterFile); + if (file.exists()) { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String line; + while ((line = reader.readLine()) != null) { + String[] parts = line.split(":"); + if (parts.length == 4) { + String groupIdPattern = parts[0].replace("*", ".*"); + String artifactIdPattern = parts[1].replace("*", ".*"); + String versionPattern = parts[2].replace("*", ".*"); + String packagingPattern = parts[3].replace("*", ".*"); + + Pattern pattern = Pattern.compile(groupIdPattern + ":" + artifactIdPattern + ":" + + versionPattern + ":" + packagingPattern); + + gavPatterns.add(new GavPattern(pattern)); + } + } + } + } catch (IOException e) { + logger.error("Failed to load GAVs from filter file", e); + } + } + return gavPatterns; + } + + public boolean canDeployGav(Gav gav, int threadPoolSize) { + + if (gavPatterns == null || gavPatterns.isEmpty()) { + return true; + } + + try (GavMatcherExecutor gavMatcherExecutor = new GavMatcherExecutor(threadPoolSize)) { + List> futures = gavMatcherExecutor.evaluateGav(gav, gavPatterns); + for (Future future : futures) { + if (future.get()) { + return true; + } + } + } catch (Exception e) { + logger.error("Error evaluating GAV {} against patterns", gav, e); + } + return false; + } } diff --git a/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/MavenRepositoryProvisioner.java b/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/MavenRepositoryProvisioner.java index 55076fc..08219dd 100644 --- a/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/MavenRepositoryProvisioner.java +++ b/maven-repository-provisioner/src/main/java/com/simpligility/maven/provisioner/MavenRepositoryProvisioner.java @@ -115,7 +115,7 @@ private static ArtifactRetriever retrieveArtifacts() { private static MavenRepositoryDeployer deployArtifacts() { logger.info("Artifact deployment starting."); - MavenRepositoryDeployer helper = new MavenRepositoryDeployer(cacheDirectory); + MavenRepositoryDeployer helper = new MavenRepositoryDeployer(cacheDirectory, config.getDeployFilterFile()); helper.deployToRemote( config.getTargetUrl(), config.getUsername(), diff --git a/maven-repository-provisioner/src/test/java/com/simpligility/maven/GavMatcherTest.java b/maven-repository-provisioner/src/test/java/com/simpligility/maven/GavMatcherTest.java new file mode 100644 index 0000000..2b6edc1 --- /dev/null +++ b/maven-repository-provisioner/src/test/java/com/simpligility/maven/GavMatcherTest.java @@ -0,0 +1,62 @@ +package com.simpligility.maven; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class GavMatcherTest { + + @Test + public void testCall_MatchingPattern() throws ExecutionException, InterruptedException { + GavPattern pattern = new GavPattern(Pattern.compile("org\\.apache\\.maven\\.resolver:.*:.*:.*")); + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + GavMatcher matcher = new GavMatcher(gav, pattern); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future result = executor.submit(matcher); + assertTrue(result.get()); + executor.shutdown(); + } + + @Test + public void testCall_NonMatchingPattern() throws ExecutionException, InterruptedException { + GavPattern pattern = new GavPattern(Pattern.compile("com\\.simpligility:.*:.*:.*")); + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + GavMatcher matcher = new GavMatcher(gav, pattern); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future result = executor.submit(matcher); + assertFalse(result.get()); + executor.shutdown(); + } + + @Test + public void testCall_EmptyPattern() throws ExecutionException, InterruptedException { + GavPattern pattern = new GavPattern(Pattern.compile("")); + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + GavMatcher matcher = new GavMatcher(gav, pattern); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future result = executor.submit(matcher); + assertFalse(result.get()); + executor.shutdown(); + } + + @Test + public void testCall_NullGav() throws ExecutionException, InterruptedException { + GavPattern pattern = new GavPattern(Pattern.compile("org\\.apache\\.maven\\.resolver:.*:.*:.*")); + GavMatcher matcher = new GavMatcher(null, pattern); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future result = executor.submit(matcher); + assertFalse(result.get()); + executor.shutdown(); + } +} diff --git a/maven-repository-provisioner/src/test/java/com/simpligility/maven/GavPatternTest.java b/maven-repository-provisioner/src/test/java/com/simpligility/maven/GavPatternTest.java new file mode 100644 index 0000000..5474531 --- /dev/null +++ b/maven-repository-provisioner/src/test/java/com/simpligility/maven/GavPatternTest.java @@ -0,0 +1,38 @@ +package com.simpligility.maven; + +import java.util.regex.Pattern; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class GavPatternTest { + + @Test + public void testMatches_ValidPattern() { + GavPattern pattern = new GavPattern(Pattern.compile("org\\.apache\\.maven\\.resolver:.*:.*:.*")); + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + assertTrue(pattern.matches(gav)); + } + + @Test + public void testMatches_InvalidPattern() { + GavPattern pattern = new GavPattern(Pattern.compile("com\\.simpligility:.*:.*:.*")); + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + assertFalse(pattern.matches(gav)); + } + + @Test + public void testMatches_EmptyPattern() { + GavPattern pattern = new GavPattern(Pattern.compile("")); + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + assertFalse(pattern.matches(gav)); + } + + @Test + public void testMatches_NullGav() { + GavPattern pattern = new GavPattern(Pattern.compile("org\\.apache\\.maven\\.resolver:.*:.*:.*")); + assertFalse(pattern.matches(null)); + } +} diff --git a/maven-repository-provisioner/src/test/java/com/simpligility/maven/GavTest.java b/maven-repository-provisioner/src/test/java/com/simpligility/maven/GavTest.java new file mode 100644 index 0000000..b68c283 --- /dev/null +++ b/maven-repository-provisioner/src/test/java/com/simpligility/maven/GavTest.java @@ -0,0 +1,30 @@ +package com.simpligility.maven; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class GavTest { + + @Test + public void testEquals() { + Gav gav1 = new Gav("groupId", "artifactId", "version", "jar"); + Gav gav2 = new Gav("groupId", "artifactId", "version", "jar"); + Gav gav3 = new Gav("groupId", "artifactId", "differentVersion", "jar"); + + assertTrue(gav1.equals(gav2)); + assertFalse(gav1.equals(gav3)); + assertFalse(gav1.equals(null)); + assertFalse(gav1.equals(new Object())); + } + + @Test + public void testHashCode() { + Gav gav1 = new Gav("groupId", "artifactId", "version", "jar"); + Gav gav2 = new Gav("groupId", "artifactId", "version", "jar"); + Gav gav3 = new Gav("groupId", "artifactId", "differentVersion", "jar"); + + assertEquals(gav1.hashCode(), gav2.hashCode()); + assertNotEquals(gav1.hashCode(), gav3.hashCode()); + } +} diff --git a/maven-repository-provisioner/src/test/java/com/simpligility/maven/provisioner/GavMatcherExecutorTest.java b/maven-repository-provisioner/src/test/java/com/simpligility/maven/provisioner/GavMatcherExecutorTest.java new file mode 100644 index 0000000..ea8ed2c --- /dev/null +++ b/maven-repository-provisioner/src/test/java/com/simpligility/maven/provisioner/GavMatcherExecutorTest.java @@ -0,0 +1,127 @@ +package com.simpligility.maven.provisioner; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.simpligility.maven.Gav; +import com.simpligility.maven.GavPattern; +import org.junit.Test; + +public class GavMatcherExecutorTest { + + @Test + public void testEvaluateGav_MatchingPattern() throws ExecutionException, InterruptedException { + Set patterns = new HashSet<>(); + patterns.add(new GavPattern(Pattern.compile("org\\.apache\\.maven\\.resolver:.*:.*:.*"))); + + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + + try (GavMatcherExecutor executor = new GavMatcherExecutor(4)) { + List> results = executor.evaluateGav(gav, patterns); + boolean matchFound = false; + for (CompletableFuture result : results) { + if (result.get()) { + matchFound = true; + break; + } + } + assertTrue(matchFound); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + public void testEvaluateGav_NonMatchingPattern() throws ExecutionException, InterruptedException { + Set patterns = new HashSet<>(); + patterns.add(new GavPattern(Pattern.compile("com\\.simpligility:.*:.*:.*"))); + + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + + try (GavMatcherExecutor executor = new GavMatcherExecutor(4)) { + List> results = executor.evaluateGav(gav, patterns); + boolean matchFound = false; + for (CompletableFuture result : results) { + if (result.get()) { + matchFound = true; + break; + } + } + assertFalse(matchFound); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + public void testEvaluateGav_EmptyPatternSet() throws ExecutionException, InterruptedException { + Set patterns = new HashSet<>(); + + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + + try (GavMatcherExecutor executor = new GavMatcherExecutor(4)) { + List> results = executor.evaluateGav(gav, patterns); + boolean matchFound = false; + for (CompletableFuture result : results) { + if (result.get()) { + matchFound = true; + break; + } + } + assertFalse(matchFound); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + public void testEvaluateGav_NullGav() throws ExecutionException, InterruptedException { + Set patterns = new HashSet<>(); + patterns.add(new GavPattern(Pattern.compile("org\\.apache\\.maven\\.resolver:.*:.*:.*"))); + + try (GavMatcherExecutor executor = new GavMatcherExecutor(4)) { + List> results = executor.evaluateGav(null, patterns); + boolean matchFound = false; + for (CompletableFuture result : results) { + if (result.get()) { + matchFound = true; + break; + } + } + assertFalse(matchFound); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + public void testEvaluateGav_MultiplePatternsWithDifferentResults() throws ExecutionException, InterruptedException { + Set patterns = new HashSet<>(); + patterns.add(new GavPattern(Pattern.compile("org\\.apache\\.maven\\.resolver:.*:.*:.*"))); // Should match + patterns.add(new GavPattern(Pattern.compile("com\\.simpligility:.*:.*:.*"))); // Should not match + patterns.add(new GavPattern(Pattern.compile("org\\.apache\\.commons:.*:.*:.*"))); // Should not match + + Gav gav = new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"); + + try (GavMatcherExecutor executor = new GavMatcherExecutor(4)) { + List> results = executor.evaluateGav(gav, patterns); + boolean matchFound = false; + for (CompletableFuture result : results) { + if (result.get()) { + matchFound = true; + break; + } + } + assertTrue(matchFound); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/maven-repository-provisioner/src/test/java/com/simpligility/maven/provisioner/MavenRepositoryDeployerTest.java b/maven-repository-provisioner/src/test/java/com/simpligility/maven/provisioner/MavenRepositoryDeployerTest.java new file mode 100644 index 0000000..ef21726 --- /dev/null +++ b/maven-repository-provisioner/src/test/java/com/simpligility/maven/provisioner/MavenRepositoryDeployerTest.java @@ -0,0 +1,74 @@ +package com.simpligility.maven.provisioner; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Set; + +import static org.junit.Assert.*; + +import com.simpligility.maven.Gav; +import com.simpligility.maven.GavPattern; +import org.junit.Before; +import org.junit.Test; + +public class MavenRepositoryDeployerTest { + + private MavenRepositoryDeployer deployer; + + @Before + public void setUp() { + deployer = new MavenRepositoryDeployer(); + } + + @Test + public void testLoadGavsFromFilterFile_ValidFile() throws IOException { + File tempFile = createTempFile("org.apache.maven.resolver:*:*:*\n"); + Set gavPatterns = deployer.loadGavPatternsFromFilterFile(tempFile.getAbsolutePath()); + assertEquals(1, gavPatterns.size()); + assertTrue(gavPatterns + .iterator() + .next() + .matches(new Gav("org.apache.maven.resolver", "artifactId", "version", "jar"))); + tempFile.delete(); + } + + @Test + public void testLoadGavsFromFilterFile_EmptyFile() throws IOException { + File tempFile = createTempFile(""); + Set gavPatterns = deployer.loadGavPatternsFromFilterFile(tempFile.getAbsolutePath()); + assertTrue(gavPatterns.isEmpty()); + tempFile.delete(); + } + + @Test + public void testLoadGavsFromFilterFile_InvalidFormat() throws IOException { + File tempFile = createTempFile("invalid:format\n"); + Set gavPatterns = deployer.loadGavPatternsFromFilterFile(tempFile.getAbsolutePath()); + assertTrue(gavPatterns.isEmpty()); + tempFile.delete(); + } + + @Test + public void testLoadGavsFromFilterFile_MixedValidAndInvalid() throws IOException { + File tempFile = createTempFile("org.apache.maven:*:*:*\ninvalid:format\n"); + Set gavPatterns = deployer.loadGavPatternsFromFilterFile(tempFile.getAbsolutePath()); + assertEquals(1, gavPatterns.size()); + assertTrue(gavPatterns.iterator().next().matches(new Gav("org.apache.maven", "artifactId", "version", "jar"))); + tempFile.delete(); + } + + @Test + public void testLoadGavsFromFilterFile_NonExistentFile() { + Set gavPatterns = deployer.loadGavPatternsFromFilterFile("nonexistentfile.txt"); + assertTrue(gavPatterns.isEmpty()); + } + + private File createTempFile(String content) throws IOException { + File tempFile = File.createTempFile("gavFilter", ".txt"); + try (FileWriter writer = new FileWriter(tempFile)) { + writer.write(content); + } + return tempFile; + } +}