From 45fd19c93ef21b7c56f81a06523ba2dbf76837ab Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Fri, 17 May 2024 07:54:11 +0200 Subject: [PATCH] Platform gen task scheduler API and a bit more of concurrent tasks --- .../quarkus/bom/decomposer/BomDecomposer.java | 46 ++--- .../bom/task/ConcurrentTaskScheduler.java | 51 +++++ .../io/quarkus/bom/task/PlatformGenTask.java | 5 + .../bom/task/PlatformGenTaskScheduler.java | 21 ++ .../bom/task/SequentialTaskScheduler.java | 31 +++ .../GeneratePlatformProjectMojo.java | 191 +++++++++--------- .../platform/ReportIndexPageGenerator.java | 58 +++--- 7 files changed, 249 insertions(+), 154 deletions(-) create mode 100644 bom-decomposer/src/main/java/io/quarkus/bom/task/ConcurrentTaskScheduler.java create mode 100644 bom-decomposer/src/main/java/io/quarkus/bom/task/PlatformGenTask.java create mode 100644 bom-decomposer/src/main/java/io/quarkus/bom/task/PlatformGenTaskScheduler.java create mode 100644 bom-decomposer/src/main/java/io/quarkus/bom/task/SequentialTaskScheduler.java diff --git a/bom-decomposer/src/main/java/io/quarkus/bom/decomposer/BomDecomposer.java b/bom-decomposer/src/main/java/io/quarkus/bom/decomposer/BomDecomposer.java index 66f3674f..479e2043 100644 --- a/bom-decomposer/src/main/java/io/quarkus/bom/decomposer/BomDecomposer.java +++ b/bom-decomposer/src/main/java/io/quarkus/bom/decomposer/BomDecomposer.java @@ -5,6 +5,7 @@ import io.quarkus.bom.resolver.ArtifactNotFoundException; import io.quarkus.bom.resolver.ArtifactResolver; import io.quarkus.bom.resolver.ArtifactResolverProvider; +import io.quarkus.bom.task.PlatformGenTaskScheduler; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; @@ -15,9 +16,6 @@ import io.quarkus.maven.dependency.ArtifactCoords; import java.nio.file.Path; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.concurrent.Phaser; import org.apache.maven.model.DistributionManagement; import org.apache.maven.model.Model; import org.apache.maven.project.ProjectBuildingRequest; @@ -169,38 +167,26 @@ private DecomposedBom decompose() throws BomDecomposerException { bomBuilder.bomArtifact(bomArtifact); //bomBuilder.bomSource(PomSource.of(resolve(bomArtifact).getFile().toPath())); var artifacts = this.artifacts == null ? bomManagedDeps() : this.artifacts; - if (isParallelProcessing()) { - addConcurrently(bomBuilder, artifacts); - } else { + + var scheduler = PlatformGenTaskScheduler.getInstance(); + try { for (Dependency dep : artifacts) { - addDependency(bomBuilder, dep); + scheduler.schedule(() -> addDependency(bomBuilder, dep)); } + scheduler.waitForCompletion(); + } catch (BomDecomposerException e) { + throw e; + } catch (Exception e) { + throw new BomDecomposerException("Failed to process dependency constraints", e); } - return transformer == null ? bomBuilder.build() : transformer.transform(bomBuilder.build()); - } - - private void addConcurrently(DecomposedBomBuilder bomBuilder, Collection deps) { - final Queue> failed = new ConcurrentLinkedDeque<>(); - var phaser = new Phaser(1); - for (var dep : deps) { - phaser.register(); - CompletableFuture.runAsync(() -> { - try { - addDependency(bomBuilder, dep); - } catch (Exception e) { - failed.add(Map.entry(dep, e)); - } finally { - phaser.arriveAndDeregister(); - } - }); - } - phaser.arriveAndAwaitAdvance(); - if (!failed.isEmpty()) { - for (var d : failed) { - logger.error("Failed to process dependency " + d.getKey(), d.getValue()); + if (scheduler.hasErrors()) { + for (var e : scheduler.getErrors()) { + logger.error("Failed to process dependency constraint", e); } - throw new RuntimeException("Failed to process dependencies reported above"); + throw new BomDecomposerException("Failed to process dependency constraints reported above"); } + + return transformer == null ? bomBuilder.build() : transformer.transform(bomBuilder.build()); } private void addDependency(DecomposedBomBuilder bomBuilder, Dependency dep) throws BomDecomposerException { diff --git a/bom-decomposer/src/main/java/io/quarkus/bom/task/ConcurrentTaskScheduler.java b/bom-decomposer/src/main/java/io/quarkus/bom/task/ConcurrentTaskScheduler.java new file mode 100644 index 00000000..a94c234a --- /dev/null +++ b/bom-decomposer/src/main/java/io/quarkus/bom/task/ConcurrentTaskScheduler.java @@ -0,0 +1,51 @@ +package io.quarkus.bom.task; + +import java.util.Collection; +import java.util.Deque; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.Phaser; + +public class ConcurrentTaskScheduler implements PlatformGenTaskScheduler { + + private final Phaser phaser = new Phaser(1); + private final Deque errors = new ConcurrentLinkedDeque<>(); + private final Deque finalizingTasks = new ConcurrentLinkedDeque<>(); + + @Override + public void schedule(PlatformGenTask task) { + phaser.register(); + CompletableFuture.runAsync(() -> { + try { + task.run(); + } catch (Exception e) { + errors.add(e); + } finally { + phaser.arriveAndDeregister(); + } + }); + } + + @Override + public void addFinializingTask(PlatformGenTask task) { + finalizingTasks.add(task); + } + + @Override + public void waitForCompletion() throws Exception { + phaser.arriveAndAwaitAdvance(); + for (var t : finalizingTasks) { + t.run(); + } + } + + @Override + public boolean hasErrors() { + return !errors.isEmpty(); + } + + @Override + public Collection getErrors() { + return errors; + } +} diff --git a/bom-decomposer/src/main/java/io/quarkus/bom/task/PlatformGenTask.java b/bom-decomposer/src/main/java/io/quarkus/bom/task/PlatformGenTask.java new file mode 100644 index 00000000..ce5006e3 --- /dev/null +++ b/bom-decomposer/src/main/java/io/quarkus/bom/task/PlatformGenTask.java @@ -0,0 +1,5 @@ +package io.quarkus.bom.task; + +public interface PlatformGenTask { + void run() throws Exception; +} diff --git a/bom-decomposer/src/main/java/io/quarkus/bom/task/PlatformGenTaskScheduler.java b/bom-decomposer/src/main/java/io/quarkus/bom/task/PlatformGenTaskScheduler.java new file mode 100644 index 00000000..1b7648d8 --- /dev/null +++ b/bom-decomposer/src/main/java/io/quarkus/bom/task/PlatformGenTaskScheduler.java @@ -0,0 +1,21 @@ +package io.quarkus.bom.task; + +import io.quarkus.bom.decomposer.BomDecomposer; +import java.util.Collection; + +public interface PlatformGenTaskScheduler { + + static PlatformGenTaskScheduler getInstance() { + return BomDecomposer.isParallelProcessing() ? new ConcurrentTaskScheduler() : new SequentialTaskScheduler(); + } + + void schedule(PlatformGenTask task) throws Exception; + + void addFinializingTask(PlatformGenTask task) throws Exception; + + void waitForCompletion() throws Exception; + + boolean hasErrors(); + + Collection getErrors(); +} diff --git a/bom-decomposer/src/main/java/io/quarkus/bom/task/SequentialTaskScheduler.java b/bom-decomposer/src/main/java/io/quarkus/bom/task/SequentialTaskScheduler.java new file mode 100644 index 00000000..fd80010f --- /dev/null +++ b/bom-decomposer/src/main/java/io/quarkus/bom/task/SequentialTaskScheduler.java @@ -0,0 +1,31 @@ +package io.quarkus.bom.task; + +import java.util.Collection; +import java.util.List; + +public class SequentialTaskScheduler implements PlatformGenTaskScheduler { + + @Override + public void schedule(PlatformGenTask task) throws Exception { + task.run(); + } + + @Override + public void addFinializingTask(PlatformGenTask task) throws Exception { + task.run(); + } + + @Override + public void waitForCompletion() { + } + + @Override + public boolean hasErrors() { + return false; + } + + @Override + public Collection getErrors() { + return List.of(); + } +} diff --git a/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/platformgen/GeneratePlatformProjectMojo.java b/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/platformgen/GeneratePlatformProjectMojo.java index 528ef6c9..bc781d91 100644 --- a/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/platformgen/GeneratePlatformProjectMojo.java +++ b/maven-plugin/src/main/java/io/quarkus/bom/decomposer/maven/platformgen/GeneratePlatformProjectMojo.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.quarkus.bom.PomSource; -import io.quarkus.bom.decomposer.BomDecomposer; import io.quarkus.bom.decomposer.BomDecomposerException; import io.quarkus.bom.decomposer.DecomposedBom; import io.quarkus.bom.decomposer.DecomposedBomHtmlReportGenerator; @@ -33,6 +32,7 @@ import io.quarkus.bom.resolver.ArtifactResolver; import io.quarkus.bom.resolver.ArtifactResolverProvider; import io.quarkus.bom.resolver.EffectiveModelResolver; +import io.quarkus.bom.task.PlatformGenTaskScheduler; import io.quarkus.bootstrap.BootstrapConstants; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext; import io.quarkus.bootstrap.resolver.maven.BootstrapMavenException; @@ -50,6 +50,7 @@ import io.quarkus.registry.catalog.ExtensionCatalog; import io.quarkus.registry.util.PlatformArtifacts; import java.io.*; +import java.net.MalformedURLException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -61,7 +62,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -71,9 +71,6 @@ import java.util.Properties; import java.util.Set; import java.util.StringJoiner; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.concurrent.Phaser; import java.util.stream.Collectors; import javax.xml.transform.Result; import javax.xml.transform.Source; @@ -263,17 +260,12 @@ public void execute() throws MojoExecutionException, MojoFailureException { persistPom(pom); generateUniversalPlatformModule(pom); - - for (PlatformMemberImpl member : members.values()) { - generateMemberModule(member, pom); - } - - for (PlatformMemberImpl member : members.values()) { - generatePlatformDescriptorModule(member.descriptorCoords(), member.baseModel, - quarkusCore.getInputBom().equals(member.getInputBom()), - platformConfig.getAttachedMavenPlugin(), member); - generatePlatformPropertiesModule(member, true); - persistPom(member.baseModel); + try { + generateMemberModules(pom); + } catch (MojoExecutionException e) { + throw e; + } catch (Exception e) { + throw new MojoExecutionException("Failed to generate platform project", e); } if (dependenciesToBuild != null) { @@ -301,10 +293,42 @@ public void execute() throws MojoExecutionException, MojoFailureException { persistPom(pom); recordUpdatedBoms(); - generateBomReports(); generateDominoCliConfig(); } + private void generateMemberModules(Model parentPom) throws Exception { + final PlatformGenTaskScheduler scheduler = PlatformGenTaskScheduler.getInstance(); + for (PlatformMemberImpl member : members.values()) { + final String moduleName = getArtifactIdBase(member.getGeneratedPlatformBom().getArtifactId()); + parentPom.addModule(moduleName); + scheduler.schedule(() -> generateMemberModule(parentPom, member, moduleName, scheduler)); + } + scheduler.schedule(() -> generateBomReports(scheduler)); + scheduler.waitForCompletion(); + if (scheduler.hasErrors()) { + for (var e : scheduler.getErrors()) { + getLog().error(e); + } + throw new MojoExecutionException("Failed to generate platform project, please see the errors logged above"); + } + } + + private void generateMemberModule(Model parentPom, PlatformMemberImpl member, String moduleName, + PlatformGenTaskScheduler scheduler) throws Exception { + generateMemberModule(member, moduleName, parentPom); + generateMemberBom(member); + if (member.config().hasTests()) { + generateMemberIntegrationTestsModule(member, scheduler); + } + scheduler.schedule(() -> { + generatePlatformDescriptorModule(member.descriptorCoords(), member.baseModel, + quarkusCore.getInputBom().equals(member.getInputBom()), + platformConfig.getAttachedMavenPlugin(), member); + generatePlatformPropertiesModule(member, true); + }); + scheduler.addFinializingTask(() -> persistPom(member.baseModel)); + } + private static void setParentVersion(Model model, Model parentModel) { var parent = model.getParent(); var version = parentModel.getVersion(); @@ -318,44 +342,51 @@ private static void setParentVersion(Model model, Model parentModel) { parent.setVersion(version); } - private void generateBomReports() throws MojoExecutionException { + private void generateBomReports(PlatformGenTaskScheduler scheduler) throws Exception { if (!(platformConfig.isGenerateBomReports() || platformConfig.getGenerateBomReportsZip() != null)) { return; } final Path reportsOutputDir = reportsDir.toPath(); // reset the resolver to pick up all the generated platform modules //resetResolver(); - try (ReportIndexPageGenerator index = new ReportIndexPageGenerator( - reportsOutputDir.resolve("index.html"))) { + final ReportIndexPageGenerator index; + try { + index = new ReportIndexPageGenerator(reportsOutputDir.resolve("index.html")); + } catch (IOException e) { + throw new MojoExecutionException(e); + } - final Path releasesReport = reportsOutputDir.resolve("main").resolve("generated-releases.html"); - generateReleasesReport(universalGeneratedBom, releasesReport); + final Path releasesReport = reportsOutputDir.resolve("main").resolve("generated-releases.html"); + generateReleasesReport(universalGeneratedBom, releasesReport); + try { index.universalBom(universalPlatformBomXml.toUri().toURL(), universalGeneratedBom, releasesReport); + } catch (MalformedURLException e) { + throw new MojoExecutionException(e); + } - var artifactResolver = ArtifactResolverProvider.get(getWorkspaceAwareMavenResolver()); - for (PlatformMemberImpl member : members.values()) { - if (member.getInputBom() == null) { - continue; - } - generateBomReports(member.originalBom, member.generatedBom, + var artifactResolver = ArtifactResolverProvider.get(getWorkspaceAwareMavenResolver()); + for (PlatformMemberImpl member : members.values()) { + if (member.getInputBom() != null) { + scheduler.schedule(() -> generateBomReports(member.originalBom, member.generatedBom, reportsOutputDir.resolve(member.config().getName().toLowerCase()), index, - member.generatedPomFile, artifactResolver); + member.generatedPomFile, artifactResolver)); } - } catch (Exception e) { - throw new MojoExecutionException("Failed to generate platform member BOM reports", e); } + scheduler.addFinializingTask(index::close); if (platformConfig.getGenerateBomReportsZip() != null) { - Path zip = Paths.get(platformConfig.getGenerateBomReportsZip()); - if (!zip.isAbsolute()) { - zip = reportsOutputDir.getParent().resolve(zip); - } - try { - Files.createDirectories(zip.getParent()); - ZipUtils.zip(reportsOutputDir, zip); - } catch (Exception e) { - throw new MojoExecutionException("Failed to ZIP platform member BOM reports", e); - } + scheduler.addFinializingTask(() -> { + Path zip = Paths.get(platformConfig.getGenerateBomReportsZip()); + if (!zip.isAbsolute()) { + zip = reportsOutputDir.getParent().resolve(zip); + } + try { + Files.createDirectories(zip.getParent()); + ZipUtils.zip(reportsOutputDir, zip); + } catch (Exception e) { + throw new MojoExecutionException("Failed to ZIP platform member BOM reports", e); + } + }); } } @@ -873,14 +904,18 @@ private static List sortAsString(Collection col) { } private static void generateReleasesReport(DecomposedBom originalBom, Path outputFile) - throws BomDecomposerException { - originalBom.visit(DecomposedBomHtmlReportGenerator.builder(outputFile) - .skipOriginsWithSingleRelease().build()); + throws MojoExecutionException { + try { + originalBom.visit(DecomposedBomHtmlReportGenerator.builder(outputFile) + .skipOriginsWithSingleRelease().build()); + } catch (BomDecomposerException e) { + throw new MojoExecutionException("Failed to generate report " + outputFile, e); + } } private static void generateBomReports(DecomposedBom originalBom, DecomposedBom generatedBom, Path outputDir, ReportIndexPageGenerator index, final Path platformBomXml, ArtifactResolver resolver) - throws BomDecomposerException { + throws MojoExecutionException { final BomDiff.Config config = BomDiff.config(); config.resolver(resolver); if (originalBom.bomResolver() != null && originalBom.bomResolver().isResolved()) { @@ -1505,9 +1540,8 @@ private void generateMavenRepoZipModule(Model parentPom) throws MojoExecutionExc persistPom(pom); } - private void generateMemberModule(PlatformMemberImpl member, Model parentPom) throws MojoExecutionException { - - final String moduleName = getArtifactIdBase(member.getGeneratedPlatformBom().getArtifactId()); + private void generateMemberModule(PlatformMemberImpl member, String moduleName, Model parentPom) + throws MojoExecutionException { final Model pom = newModel(); @@ -1521,7 +1555,6 @@ private void generateMemberModule(PlatformMemberImpl member, Model parentPom) th pom.setPackaging(ArtifactCoords.TYPE_POM); pom.setName(getNameBase(parentPom) + " " + member.config().getName() + " - Parent"); - parentPom.addModule(moduleName); final File pomXml = getPomFile(parentPom, moduleName); pom.setPomFile(pomXml); @@ -1535,12 +1568,6 @@ private void generateMemberModule(PlatformMemberImpl member, Model parentPom) th member.baseModel = pom; - generateMemberBom(member); - - if (member.config().hasTests()) { - generateMemberIntegrationTestsModule(member); - } - if (member.config().isHidden() || platformConfig.getRelease() != null && platformConfig.getRelease().isOnlyChangedMembers() @@ -1709,8 +1736,8 @@ private int pomLineContaining(String text, int fromLine, int toLine) { return fromLine == upperLimit ? -1 : fromLine; } - private void generateMemberIntegrationTestsModule(PlatformMemberImpl member) - throws MojoExecutionException { + private void generateMemberIntegrationTestsModule(PlatformMemberImpl member, PlatformGenTaskScheduler scheduler) + throws Exception { final Model parentPom = member.baseModel; final String moduleName = "integration-tests"; @@ -1798,17 +1825,6 @@ private void generateMemberIntegrationTestsModule(PlatformMemberImpl member) } } - final boolean parallelProcessing = testConfigs.size() > 1 && BomDecomposer.isParallelProcessing(); - final Phaser phaser; - final Deque errors; - if (parallelProcessing) { - phaser = new Phaser(1); - errors = new ConcurrentLinkedDeque<>(); - } else { - phaser = null; - errors = null; - } - for (PlatformMemberTestConfig testConfig : testConfigs.values()) { if (member.config().getDefaultTestConfig() != null) { testConfig.applyDefaults(member.config().getDefaultTestConfig()); @@ -1828,35 +1844,14 @@ private void generateMemberIntegrationTestsModule(PlatformMemberImpl member) testModuleName = testArtifact.getArtifactId(); } pom.addModule(testModuleName); - if (parallelProcessing) { - phaser.register(); - CompletableFuture.runAsync(() -> { - try { - generateIntegrationTestModule(testModuleName, testArtifact, testConfig, pom); - } catch (MojoExecutionException e) { - errors.add(e); - } finally { - phaser.arriveAndDeregister(); - } - }); - } else { - generateIntegrationTestModule(testModuleName, testArtifact, testConfig, pom); - } - } - } - if (phaser != null) { - phaser.arriveAndAwaitAdvance(); - if (!errors.isEmpty()) { - for (var e : errors) { - getLog().error(e); - } - throw new MojoExecutionException( - "Failed to generate integration test modules, please see the errors logged above"); + scheduler.schedule(() -> generateIntegrationTestModule(testModuleName, testArtifact, testConfig, pom)); } } - Utils.skipInstallAndDeploy(pom); - persistPom(pom); + scheduler.addFinializingTask(() -> { + Utils.skipInstallAndDeploy(pom); + persistPom(pom); + }); } private Dependency getUniversalBomImport() { @@ -2420,8 +2415,10 @@ private void generateUniversalPlatformModule(Model parentPom) throws MojoExecuti private void generatePlatformDescriptorModule(ArtifactCoords descriptorCoords, Model parentPom, boolean copyQuarkusCoreMetadata, AttachedMavenPluginConfig attachedPlugin, PlatformMember member) throws MojoExecutionException { - final String moduleName = "descriptor"; + + var moduleName = "descriptor"; parentPom.addModule(moduleName); + final Path moduleDir = parentPom.getProjectDirectory().toPath().resolve(moduleName); final Model pom = newModel(); @@ -2689,7 +2686,8 @@ private void addMemberDescriptorConfig(final Model pom, final Xpp3Dom membersCon membersConfig.addChild(textDomElement("member", value)); } - private void generatePlatformPropertiesModule(PlatformMemberImpl member, boolean addPlatformReleaseConfig) + private void generatePlatformPropertiesModule(PlatformMemberImpl member, + boolean addPlatformReleaseConfig) throws MojoExecutionException { final ArtifactCoords propertiesCoords = member.propertiesCoords(); @@ -2697,6 +2695,7 @@ private void generatePlatformPropertiesModule(PlatformMemberImpl member, boolean final String moduleName = "properties"; parentPom.addModule(moduleName); + final Path moduleDir = parentPom.getProjectDirectory().toPath().resolve(moduleName); final Model pom = newModel(); diff --git a/platform-bom/src/main/java/io/quarkus/bom/platform/ReportIndexPageGenerator.java b/platform-bom/src/main/java/io/quarkus/bom/platform/ReportIndexPageGenerator.java index 752f6c73..5f488cdd 100644 --- a/platform-bom/src/main/java/io/quarkus/bom/platform/ReportIndexPageGenerator.java +++ b/platform-bom/src/main/java/io/quarkus/bom/platform/ReportIndexPageGenerator.java @@ -5,8 +5,8 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; +import java.util.Collection; +import java.util.concurrent.ConcurrentLinkedDeque; public class ReportIndexPageGenerator extends FileReportWriter implements AutoCloseable { @@ -17,17 +17,7 @@ public class ReportIndexPageGenerator extends FileReportWriter implements AutoCl private DecomposedBom mainBom; private Path mainBomReleasesHtml; - private List mainUrl = new ArrayList<>(); - private List toUrl = new ArrayList<>(); - private List toBoms = new ArrayList<>(); - private List mainReleasesHtml = new ArrayList<>(); - private List toReleasesHtml = new ArrayList<>(); - private List diffHtml = new ArrayList<>(); - - public ReportIndexPageGenerator(String name) throws IOException { - super(name); - initHtmlBody(); - } + private final Collection memberData = new ConcurrentLinkedDeque<>(); public ReportIndexPageGenerator(Path file) throws IOException { super(file); @@ -78,19 +68,17 @@ private void generateContents() throws IOException { writeTag("p", ""); openTag("table"); writeTag("caption", "text-align:left;font-weight:bold", "Extension BOMs"); - int i = 0; - while (i < toBoms.size()) { + for (var member : memberData) { openTag("tr", listBackground[backgroundIndex ^= 1]); - writeTag("td", "text-align:left;font-weight:bold;color:gray", toBoms.get(i).bomArtifact()); - writeTag("td", "text-align:left", generateAnchor(mainUrl.get(i).toExternalForm(), "original")); + writeTag("td", "text-align:left;font-weight:bold;color:gray", member.toBom.bomArtifact()); + writeTag("td", "text-align:left", generateAnchor(member.mainUrl.toExternalForm(), "original")); writeTag("td", "text-align:left", - generateAnchor(mainReleasesHtml.get(i).toUri().toURL().toExternalForm(), "decomposed")); - writeTag("td", "text-align:left", generateAnchor(toUrl.get(i).toExternalForm(), "generated")); + generateAnchor(member.mainReleasesHtml.toUri().toURL().toExternalForm(), "decomposed")); + writeTag("td", "text-align:left", generateAnchor(member.toUrl.toExternalForm(), "generated")); writeTag("td", "text-align:left", - generateAnchor(toReleasesHtml.get(i).toUri().toURL().toExternalForm(), "decomposed")); - writeTag("td", "text-align:left", generateAnchor(diffHtml.get(i).toUri().toURL().toExternalForm(), "diff")); + generateAnchor(member.toReleasesHtml.toUri().toURL().toExternalForm(), "decomposed")); + writeTag("td", "text-align:left", generateAnchor(member.diffHtml.toUri().toURL().toExternalForm(), "diff")); closeTag("tr"); - ++i; } closeTag("table"); } @@ -103,12 +91,7 @@ public void universalBom(URL mainUrl, DecomposedBom decomposed, Path releasesHtm public void bomReport(URL mainUrl, URL toUrl, DecomposedBom toBom, Path mainReleasesHtml, Path toReleasesHtml, Path diffHtml) { - this.mainUrl.add(mainUrl); - this.toUrl.add(toUrl); - this.toBoms.add(toBom); - this.mainReleasesHtml.add(mainReleasesHtml); - this.toReleasesHtml.add(toReleasesHtml); - this.diffHtml.add(diffHtml); + memberData.add(new MemberData(mainUrl, toUrl, toBom, mainReleasesHtml, toReleasesHtml, diffHtml)); } @Override @@ -121,4 +104,23 @@ public void close() { } super.close(); } + + private static class MemberData { + final URL mainUrl; + final URL toUrl; + final DecomposedBom toBom; + final Path mainReleasesHtml; + final Path toReleasesHtml; + final Path diffHtml; + + public MemberData(URL mainUrl, URL toUrl, DecomposedBom toBom, Path mainReleasesHtml, Path toReleasesHtml, + Path diffHtml) { + this.mainUrl = mainUrl; + this.toUrl = toUrl; + this.toBom = toBom; + this.mainReleasesHtml = mainReleasesHtml; + this.toReleasesHtml = toReleasesHtml; + this.diffHtml = diffHtml; + } + } }