From 3670fef6d460e7e11c4fa9780aafc2ad78969428 Mon Sep 17 00:00:00 2001 From: skhokhlov Date: Tue, 10 Sep 2024 17:14:07 +0100 Subject: [PATCH] refactor: decouple methods and add debug logging Signed-off-by: skhokhlov --- .../org/cyclonedx/gradle/CycloneDxTask.java | 207 ++++++++++-------- 1 file changed, 113 insertions(+), 94 deletions(-) diff --git a/src/main/java/org/cyclonedx/gradle/CycloneDxTask.java b/src/main/java/org/cyclonedx/gradle/CycloneDxTask.java index 37bdb13..a417e87 100644 --- a/src/main/java/org/cyclonedx/gradle/CycloneDxTask.java +++ b/src/main/java/org/cyclonedx/gradle/CycloneDxTask.java @@ -93,8 +93,9 @@ public class CycloneDxTask extends DefaultTask { private static final String MESSAGE_VALIDATION_FAILURE = "The BOM does not conform to the CycloneDX BOM standard"; private static final String DEFAULT_PROJECT_TYPE = "library"; + private final Map components; - private MavenHelper mavenHelper; + private final MavenHelper mavenHelper; private final Property schemaVersion; private final Property componentName; @@ -111,12 +112,15 @@ public class CycloneDxTask extends DefaultTask { private final Property includeMetadataResolution; private OrganizationalEntity organizationalEntity; private LicenseChoice licenseChoice; - private final Map> artifactHashes = Collections.synchronizedMap(new HashMap<>()); - private final Map resolvedMavenProjects = Collections.synchronizedMap(new HashMap<>()); + private final Map> artifactHashes; + private final Map resolvedMavenProjects; public CycloneDxTask() { schemaVersion = getProject().getObjects().property(String.class); schemaVersion.convention(CycloneDxUtils.DEFAULT_SCHEMA_VERSION.getVersionString()); + if (getSchemaVersion().get().equals(Version.VERSION_10.getVersionString())) { + setIncludeBomSerialNumber(false); + } outputName = getProject().getObjects().property(String.class); outputName.convention("bom"); @@ -157,6 +161,13 @@ public CycloneDxTask() { organizationalEntity = new OrganizationalEntity(); licenseChoice = new LicenseChoice(); + artifactHashes = Collections.synchronizedMap(new HashMap<>()); + resolvedMavenProjects = Collections.synchronizedMap(new HashMap<>()); + components = new TreeMap<>(); + mavenHelper = new MavenHelper( + getLogger(), + getVersion(getSchemaVersion()), + getIncludeLicenseText().get()); } @Input @@ -362,85 +373,106 @@ public void createBom() { || getProject().getVersion().equals("")) { throw new GradleException("Project group, name, and version must be set for the root project"); } - - Version version = computeSchemaVersion(); logParameters(); getLogger().info(MESSAGE_RESOLVING_DEPS); final Set builtDependencies = allBuiltProjects(); - final Map components = new TreeMap<>(); final Map dependencies = new TreeMap<>(); final Metadata metadata = createMetadata(); - Project rootProject = getProject(); - + Project pluginProject = getProject(); org.cyclonedx.model.Dependency rootDependency = - new org.cyclonedx.model.Dependency(generatePackageUrl(rootProject)); - dependencies.put(generatePackageUrl(rootProject), rootDependency); + new org.cyclonedx.model.Dependency(generatePackageUrl(pluginProject)); + dependencies.put(generatePackageUrl(pluginProject), rootDependency); + Stream.concat(Stream.of(pluginProject), pluginProject.getSubprojects().stream()) + .forEach(project -> processProject(project, rootDependency, dependencies, builtDependencies)); + writeBom(metadata, new HashSet<>(components.values()), dependencies.values()); + } - Set projectsToScan = Stream.concat(Stream.of(rootProject), rootProject.getSubprojects().stream()) - .filter(p -> !shouldSkipProject(p)) - .collect(Collectors.toSet()); + private void processProject( + Project project, + org.cyclonedx.model.Dependency rootDependency, + Map dependencies, + Set builtDependencies) { + getLogger().debug("Processing project [{}]", project.getName()); + if (shouldSkipProject(project)) { + getLogger().debug("Project [{}] skipped due to plugin configuration", project.getName()); + return; + } + String projectReference = generatePackageUrl(project); + org.cyclonedx.model.Dependency projectDependency = new org.cyclonedx.model.Dependency(projectReference); + for (Configuration configuration : project.getConfigurations()) { + processConfiguration(configuration, projectReference, projectDependency, dependencies, builtDependencies); + } - projectsToScan.forEach(project -> { - Set configurations = project.getConfigurations().stream() - .filter(configuration -> shouldIncludeConfiguration(configuration) - && !shouldSkipConfiguration(configuration) - && DependencyUtils.canBeResolved(configuration)) + if (!getProject().equals(project)) { + rootDependency.addDependency(projectDependency); + // declare sub-project as component + Component component = generateProjectComponent(project); + String bomRef = component.getBomRef(); + components.putIfAbsent(bomRef, component); + } + } + + private void processConfiguration( + Configuration configuration, + String projectReference, + org.cyclonedx.model.Dependency moduleDependency, + Map dependencies, + Set builtDependencies) { + getLogger().debug("Processing configuration [{}]", configuration.getName()); + if (!shouldIncludeConfiguration(configuration) || shouldSkipConfiguration(configuration)) { + getLogger().debug("Configuration [{}] skipped due to plugin configuration", configuration.getName()); + return; + } + if (!configuration.isCanBeResolved()) { + getLogger().debug("Configuration [{}] because it cannot be resolved", configuration.getName()); + return; + } + final ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration(); + final Set directModuleDependencies = + configuration.getResolvedConfiguration().getFirstLevelModuleDependencies(); + + while (directModuleDependencies.stream().anyMatch(this::dependencyWithoutJarArtifact)) { + Set depWithNoArtifacts = directModuleDependencies.stream() + .filter(this::dependencyWithoutJarArtifact) .collect(Collectors.toSet()); - String projectReference = generatePackageUrl(project); - for (Configuration configuration : configurations) { - final ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration(); - final org.cyclonedx.model.Dependency moduleDependency = - new org.cyclonedx.model.Dependency(projectReference); - final Set directModuleDependencies = - configuration.getResolvedConfiguration().getFirstLevelModuleDependencies(); - - while (directModuleDependencies.stream().anyMatch(this::dependencyWithoutJarArtifact)) { - Set depWithNoArtifacts = directModuleDependencies.stream() - .filter(this::dependencyWithoutJarArtifact) - .collect(Collectors.toSet()); - - directModuleDependencies.removeAll(depWithNoArtifacts); - depWithNoArtifacts.forEach(dmd -> directModuleDependencies.addAll(dmd.getChildren())); - } + directModuleDependencies.removeAll(depWithNoArtifacts); + depWithNoArtifacts.forEach(dmd -> directModuleDependencies.addAll(dmd.getChildren())); + } - for (ResolvedDependency directModuleDependency : directModuleDependencies) { - @Nullable ResolvedArtifact directJarArtifact = getJarOrZipArtifact(directModuleDependency); - if (directJarArtifact != null) { - moduleDependency.addDependency( - new org.cyclonedx.model.Dependency(generatePackageUrl(directJarArtifact))); - buildDependencyGraph(dependencies, directModuleDependency, directJarArtifact); - } - } - dependencies.compute(projectReference, (k, v) -> { - if (v == null) { - return moduleDependency; - } else if (moduleDependency.getDependencies() != null) { - moduleDependency.getDependencies().forEach(v::addDependency); - } - return v; - }); - - resolvedConfiguration.getResolvedArtifacts().forEach(artifact -> { - String dependencyName = DependencyUtils.getDependencyName(artifact); - Component component = convertArtifact(artifact, version); - - // Resources not built as part of this Gradle project will be augmented with - // metadata from their poms - if (!builtDependencies.contains(dependencyName)) { - if (getIncludeMetadataResolution().get()) { - augmentComponentMetadata(artifact, component, dependencyName); - } - } + for (ResolvedDependency directModuleDependency : directModuleDependencies) { + @Nullable ResolvedArtifact directJarArtifact = getJarOrZipArtifact(directModuleDependency); + if (directJarArtifact != null) { + moduleDependency.addDependency( + new org.cyclonedx.model.Dependency(generatePackageUrl(directJarArtifact))); + buildDependencyGraph(dependencies, directModuleDependency, directJarArtifact); + } + } + dependencies.compute(projectReference, (k, v) -> { + if (v == null) { + return moduleDependency; + } else if (moduleDependency.getDependencies() != null) { + moduleDependency.getDependencies().forEach(v::addDependency); + } + return v; + }); + + resolvedConfiguration.getResolvedArtifacts().forEach(artifact -> { + String dependencyName = DependencyUtils.getDependencyName(artifact); + Component component = convertArtifact(artifact); - components.putIfAbsent(component.getBomRef(), component); - }); + // Resources not built as part of this Gradle project will be augmented with + // metadata from their poms + if (!builtDependencies.contains(dependencyName)) { + if (getIncludeMetadataResolution().get()) { + augmentComponentMetadata(artifact, component, dependencyName); + } } + + components.putIfAbsent(component.getBomRef(), component); }); - addSubProjectsAsComponents(rootDependency, version, projectsToScan, components); - writeBom(metadata, new HashSet<>(components.values()), dependencies.values(), version); } private Set allBuiltProjects() { @@ -461,7 +493,7 @@ private void addSubProjectsAsComponents( if (!rootProject.equals(project)) { rootDependency.addDependency(new org.cyclonedx.model.Dependency(projectReference)); // declare sub-project as component - Component component = generateProjectComponent(project, version); + Component component = generateProjectComponent(project); String bomRef = component.getBomRef(); components.putIfAbsent(bomRef, component); } @@ -472,16 +504,6 @@ private boolean dependencyWithoutJarArtifact(ResolvedDependency dependency) { return getJarOrZipArtifact(dependency) == null; } - private Version computeSchemaVersion() { - Version version = CycloneDxUtils.schemaVersion(getSchemaVersion().get()); - mavenHelper = - new MavenHelper(getLogger(), version, getIncludeLicenseText().get()); - if (version == Version.VERSION_10) { - setIncludeBomSerialNumber(false); - } - return version; - } - private Map buildDependencyGraph( Map dependenciesSoFar, ResolvedDependency resolvedDependency, @@ -637,7 +659,7 @@ private Component.Type resolveProjectType() { return Component.Type.LIBRARY; } - private Component generateProjectComponent(Project project, Version schemaVersion) { + private Component generateProjectComponent(Project project) { final Component component = new Component(); component.setGroup(project.getGroup().toString()); component.setName(project.getName()); @@ -647,14 +669,14 @@ private Component generateProjectComponent(Project project, Version schemaVersio String projectReference = generatePackageUrl(project); component.setPurl(projectReference); - if (schemaVersion.getVersion() >= 1.1) { + if (getVersion(schemaVersion).getVersion() >= 1.1) { component.setBomRef(projectReference); } return component; } - private Component convertArtifact(ResolvedArtifact artifact, Version schemaVersion) { + private Component convertArtifact(ResolvedArtifact artifact) { final Component component = new Component(); component.setGroup(artifact.getModuleVersion().getId().getGroup()); component.setName(artifact.getModuleVersion().getId().getName()); @@ -663,7 +685,7 @@ private Component convertArtifact(ResolvedArtifact artifact, Version schemaVersi getLogger().debug(MESSAGE_CALCULATING_HASHES); List hashes = artifactHashes.computeIfAbsent(artifact.getFile(), f -> { try { - return BomUtils.calculateHashes(f, schemaVersion); + return BomUtils.calculateHashes(f, getVersion(schemaVersion)); } catch (IOException e) { getLogger().error("Error encountered calculating hashes", e); } @@ -674,7 +696,7 @@ private Component convertArtifact(ResolvedArtifact artifact, Version schemaVersi final String packageUrl = generatePackageUrl(artifact); component.setPurl(packageUrl); - if (schemaVersion.getVersion() >= 1.1) { + if (getVersion(schemaVersion).getVersion() >= 1.1) { component.setModified(mavenHelper.isModified(artifact)); component.setBomRef(packageUrl); } @@ -737,18 +759,11 @@ private String generatePackageUrl(final Project project) { /** * Ported from Maven plugin. * - * @param metadata - * The CycloneDX metadata object - * @param components - * The CycloneDX components extracted from gradle dependencies - * @param version - * The CycloneDX schema version + * @param metadata The CycloneDX metadata object + * @param components The CycloneDX components extracted from gradle dependencies */ protected void writeBom( - Metadata metadata, - Set components, - Collection dependencies, - Version version) + Metadata metadata, Set components, Collection dependencies) throws GradleException { try { getLogger().info(MESSAGE_CREATING_BOM); @@ -757,7 +772,7 @@ protected void writeBom( boolean includeSerialNumber = getBooleanParameter( "cyclonedx.includeBomSerialNumber", getIncludeBomSerialNumber().get()); - + Version version = getVersion(schemaVersion); if (Version.VERSION_10 != version && includeSerialNumber) { bom.setSerialNumber("urn:uuid:" + UUID.randomUUID()); } @@ -767,7 +782,7 @@ protected void writeBom( if (outputFormat.get().equals("all") || outputFormat.get().equals("xml")) { writeXMLBom(version, bom); } - if (CycloneDxUtils.schemaVersion(getSchemaVersion().get()).getVersion() >= 1.2 + if (getVersion(getSchemaVersion()).getVersion() >= 1.2 && (outputFormat.get().equals("all") || outputFormat.get().equals("json"))) { writeJSONBom(version, bom); } @@ -777,6 +792,10 @@ protected void writeBom( } } + private Version getVersion(Property schemaVersion) { + return CycloneDxUtils.schemaVersion(schemaVersion.get()); + } + private void writeXMLBom(final Version schemaVersion, final Bom bom) throws GeneratorException, ParserConfigurationException, IOException { final BomXmlGenerator bomGenerator = BomGeneratorFactory.createXml(schemaVersion, bom);