Repository: CycloneDX/cyclonedx-maven-plugin (java)

Issue:
Sort log output for easier understanding of excluded/not depended upon artifacts
We run the plugin on rather large aggregators, and reading the list of excluded artifacts or the list of not depended upon artifacts is hard, as those are in "random looking" order (which is in fact the build order calculated by Maven). That output would be easier to understand by sorting.

Pull request description:
Make log output more easy to understand by sorting
Sort the log output for excluded artifacts as well as not depended upon artifacts. That makes comparing multiple builds much easier than reading the "randomly sorted" looking output (it's not really random, but driven by the dependency order).

There is no functional change in which order the plugin iterates dependencies and calculates entries in the SBOM. Only the log output is sorted:

![grafik](https://github.com/user-attachments/assets/ad05d0c3-f69e-40e5-afb3-d358b46372d1)

fixes #541 

Side note: It's impossible to run integration tests behind a company proxy because of https://github.com/takari/takari-plugin-testing-project/issues/16

Patch:
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index ac01cc9..83893cb 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -8,14 +8,14 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v4.1.7
+    - uses: actions/checkout@v4.2.1
     - name: Set up JDK 1.8
       uses: actions/setup-java@v4
       with:
         distribution: 'adopt'
         java-version: '8'
     - name: Build with Maven
-      run: mvn --no-transfer-progress package site
+      run: mvn --no-transfer-progress verify site
     - name: Deploy documentation
       if: ${{ github.ref == 'refs/heads/master' }}
       uses: JamesIves/github-pages-deploy-action@releases/v3
diff --git a/README.md b/README.md
index 6a0f002..64da153 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ Default Values
         <artifactId>cyclonedx-maven-plugin</artifactId>
         <configuration>
             <projectType>library</projectType>
-            <schemaVersion>1.5</schemaVersion>
+            <schemaVersion>1.6</schemaVersion>
             <includeBomSerialNumber>true</includeBomSerialNumber>
             <includeCompileScope>true</includeCompileScope>
             <includeProvidedScope>true</includeProvidedScope>
@@ -63,7 +63,7 @@ Default Values
 </plugins>
 ```
 
-`<projectType>` default value is `library` but there are [more choices defined in the CycloneDX specification](https://cyclonedx.org/docs/1.5/json/#metadata_component_type).
+`<projectType>` default value is `library` but there are [more choices defined in the CycloneDX specification](https://cyclonedx.org/docs/1.6/json/#metadata_component_type).
 
 See also [External References](https://cyclonedx.github.io/cyclonedx-maven-plugin/external-references.html) documentation for details on this topic.
 
@@ -99,6 +99,7 @@ the CycloneDX version supported by the target system.
 
 | Version | Schema Version | Format(s) |
 |---------|----------------| --------- |
+| 2.9.x   | CycloneDX v1.6 | XML/JSON |
 | 2.8.x   | CycloneDX v1.5 | XML/JSON |
 | 2.6.x   | CycloneDX v1.4 | XML/JSON |
 | 2.5.x   | CycloneDX v1.3 | XML/JSON |
diff --git a/pom.xml b/pom.xml
index dae3dc5..f914e6f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
     <groupId>org.cyclonedx</groupId>
     <artifactId>cyclonedx-maven-plugin</artifactId>
     <packaging>maven-plugin</packaging>
-    <version>2.8.2-SNAPSHOT</version>
+    <version>2.9.1-SNAPSHOT</version>
 
     <name>CycloneDX Maven plugin</name>
     <description>The CycloneDX Maven plugin generates CycloneDX Software Bill of Materials (SBOM) containing the aggregate of all direct and transitive dependencies of a project.</description>
@@ -68,10 +68,10 @@
         <maven.compiler.target>8</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-        <project.build.outputTimestamp>2024-08-03T22:37:36Z</project.build.outputTimestamp>
+        <project.build.outputTimestamp>2024-10-08T04:31:15Z</project.build.outputTimestamp>
         <junit.version>4.13.2</junit.version>
-        <junit5.version>5.10.3</junit5.version>
-        <plugin-tools.version>3.13.1</plugin-tools.version>
+        <junit5.version>5.11.2</junit5.version>
+        <plugin-tools.version>3.15.0</plugin-tools.version>
     </properties>
 
     <scm>
@@ -106,7 +106,7 @@
         <dependency>
             <groupId>org.cyclonedx</groupId>
             <artifactId>cyclonedx-core-java</artifactId>
-            <version>8.0.3</version>
+            <version>9.0.5</version>
         </dependency>
         <dependency>
             <groupId>javax.inject</groupId>
@@ -119,12 +119,12 @@
         <dependency>
             <groupId>commons-codec</groupId>
             <artifactId>commons-codec</artifactId>
-            <version>1.17.0</version>
+            <version>1.17.1</version>
         </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
-            <version>3.14.0</version>
+            <version>3.17.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
@@ -345,7 +345,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-javadoc-plugin</artifactId>
-                    <version>3.8.0</version>
+                    <version>3.10.1</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
@@ -355,7 +355,16 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-project-info-reports-plugin</artifactId>
-                    <version>3.6.1</version>
+                    <version>3.6.2</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-resources-plugin</artifactId>
+                    <version>3.3.1</version>
+                    <configuration>
+                        <!-- https://maven.apache.org/plugins/maven-resources-plugin/examples/filtering-properties-files.html -->
+                        <propertiesEncoding>UTF-8</propertiesEncoding>
+                    </configuration>
                 </plugin>
             </plugins>
         </pluginManagement>
@@ -405,7 +414,7 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-gpg-plugin</artifactId>
-                        <version>3.2.4</version>
+                        <version>3.2.7</version>
                         <executions>
                             <execution>
                                 <id>sign-artifacts</id>
diff --git a/src/it/makeAggregateBom/api/pom.xml b/src/it/makeAggregateBom/api/pom.xml
index 5a08ec7..d954c26 100644
--- a/src/it/makeAggregateBom/api/pom.xml
+++ b/src/it/makeAggregateBom/api/pom.xml
@@ -32,7 +32,7 @@
         <dependency>
             <groupId>org.cyclonedx</groupId>
             <artifactId>cyclonedx-core-java</artifactId>
-            <version>7.2.1</version>
+            <version>9.0.5</version>
         </dependency>
     </dependencies>
 </project>
diff --git a/src/it/makeBom/pom.xml b/src/it/makeBom/pom.xml
index 9408290..73b22c5 100644
--- a/src/it/makeBom/pom.xml
+++ b/src/it/makeBom/pom.xml
@@ -72,7 +72,7 @@
         <dependency>
             <groupId>org.cyclonedx</groupId>
             <artifactId>cyclonedx-core-java</artifactId>
-            <version>7.2.1</version>
+            <version>9.0.5</version>
         </dependency>
         <dependency>
             <groupId>com.ibm.websphere.appserver.features</groupId>
diff --git a/src/it/makeBom/verify.groovy b/src/it/makeBom/verify.groovy
index a0246ca..4d04cb4 100644
--- a/src/it/makeBom/verify.groovy
+++ b/src/it/makeBom/verify.groovy
@@ -4,11 +4,13 @@ File bomFileJson = new File(basedir, "target/bom.json")
 assert bomFileXml.exists()
 assert bomFileJson.exists()
 
-assert bomFileXml.text.contains('<reference type="website"><url>https://github.com/CycloneDX/cyclonedx-maven-plugin</url></reference>')
+assert bomFileXml.text.contains('<reference type="website">\n' +
+        '          <url>https://github.com/CycloneDX/cyclonedx-maven-plugin</url>\n' +
+        '        </reference>')
 
 assert !bomFileXml.text.contains('<property name="maven.optional.unused">')
 
-assert bomFileJson.text.contains('"specVersion" : "1.5"')
+assert bomFileJson.text.contains('"specVersion" : "1.6"')
 
 // Reproducible Builds
 assert !bomFileJson.text.contains('"timestamp"')
diff --git a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
index 386383e..fe6f21f 100644
--- a/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
+++ b/src/main/java/org/cyclonedx/maven/BaseCycloneDxMojo.java
@@ -29,8 +29,8 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalysis;
 import org.codehaus.plexus.util.xml.Xpp3Dom;
-import org.cyclonedx.BomGeneratorFactory;
-import org.cyclonedx.CycloneDxSchema;
+import org.cyclonedx.Version;
+import org.cyclonedx.generators.BomGeneratorFactory;
 import org.cyclonedx.exception.GeneratorException;
 import org.cyclonedx.generators.json.BomJsonGenerator;
 import org.cyclonedx.generators.xml.BomXmlGenerator;
@@ -61,18 +61,20 @@ import java.util.Set;
 import java.util.UUID;
 
 public abstract class BaseCycloneDxMojo extends AbstractMojo {
+    static final String CYCLONEDX_PLUGIN_KEY = "org.cyclonedx:cyclonedx-maven-plugin";
+    static final String PROJECT_TYPE = "projectType";
 
     @Parameter(property = "project", readonly = true, required = true)
     private MavenProject project;
 
     /**
      * The component type associated to the SBOM metadata. See
-     * <a href="https://cyclonedx.org/docs/1.5/json/#metadata_component_type">CycloneDX reference</a> for supported
+     * <a href="https://cyclonedx.org/docs/1.6/json/#metadata_component_type">CycloneDX reference</a> for supported
      * values. 
      *
      * @since 2.0.0
      */
-    @Parameter(property = "projectType", defaultValue = "library", required = false)
+    @Parameter(property = PROJECT_TYPE, defaultValue = "library", required = false)
     private String projectType;
 
     /**
@@ -80,9 +82,9 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
      *
      * @since 2.1.0
      */
-    @Parameter(property = "schemaVersion", defaultValue = "1.5", required = false)
+    @Parameter(property = "schemaVersion", defaultValue = "1.6", required = false)
     private String schemaVersion;
-    private CycloneDxSchema.Version effectiveSchemaVersion = null;
+    private Version effectiveSchemaVersion = null;
 
     /**
      * The CycloneDX output format that should be generated (<code>xml</code>, <code>json</code> or <code>all</code>).
@@ -227,7 +229,7 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
     private String outputTimestamp;
 
     /**
-     * <a href="https://cyclonedx.org/docs/1.5/json/#metadata_component_externalReferences_items_type">External references</a>
+     * <a href="https://cyclonedx.org/docs/1.6/json/#metadata_component_externalReferences_items_type">External references</a>
      * to be added to the component the BOM describes <code>$.metadata.component.externalReferences[]</code>:
      * <pre>
      * &lt;externalReferences&gt;
@@ -262,7 +264,7 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
     protected static final String MESSAGE_CREATING_BOM = "CycloneDX: Creating BOM version %s with %d component(s)";
     static final String MESSAGE_CALCULATING_HASHES = "CycloneDX: Calculating Hashes";
     protected static final String MESSAGE_WRITING_BOM = "CycloneDX: Writing and validating BOM (%s): %s";
-    protected static final String MESSAGE_ATTACHING_BOM = "           attaching as %s-%s-cyclonedx.%s";
+    protected static final String MESSAGE_ATTACHING_BOM = "           attaching as %s-%s-%s.%s";
     protected static final String MESSAGE_VALIDATION_FAILURE = "The BOM does not conform to the CycloneDX BOM standard as defined by the XSD";
 
     /**
@@ -463,7 +465,7 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
         }
 
         if (!skipAttach) {
-            getLog().info(String.format(MESSAGE_ATTACHING_BOM, project.getArtifactId(), project.getVersion(), extension));
+            getLog().info(String.format(MESSAGE_ATTACHING_BOM, project.getArtifactId(), project.getVersion(), classifier, extension));
             mavenProjectHelper.attachArtifact(project, extension, classifier, bomFile);
         }
     }
@@ -477,20 +479,22 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
      * Resolves the CycloneDX schema the mojo has been requested to use.
      * @return the CycloneDX schema to use
      */
-    protected CycloneDxSchema.Version schemaVersion() {
+    protected Version schemaVersion() {
         if (effectiveSchemaVersion == null) {
             if ("1.0".equals(schemaVersion)) {
-                effectiveSchemaVersion = CycloneDxSchema.Version.VERSION_10;
+                effectiveSchemaVersion = Version.VERSION_10;
             } else if ("1.1".equals(schemaVersion)) {
-                effectiveSchemaVersion = CycloneDxSchema.Version.VERSION_11;
+                effectiveSchemaVersion = Version.VERSION_11;
             } else if ("1.2".equals(schemaVersion)) {
-                effectiveSchemaVersion = CycloneDxSchema.Version.VERSION_12;
+                effectiveSchemaVersion = Version.VERSION_12;
             } else if ("1.3".equals(schemaVersion)) {
-                effectiveSchemaVersion = CycloneDxSchema.Version.VERSION_13;
+                effectiveSchemaVersion = Version.VERSION_13;
             } else if ("1.4".equals(schemaVersion)) {
-                effectiveSchemaVersion = CycloneDxSchema.Version.VERSION_14;
+                effectiveSchemaVersion = Version.VERSION_14;
+            } else if ("1.5".equals(schemaVersion)) {
+                effectiveSchemaVersion = Version.VERSION_15;
             } else {
-                effectiveSchemaVersion = CycloneDxSchema.Version.VERSION_15;
+                effectiveSchemaVersion = Version.VERSION_16;
             }
         }
         return effectiveSchemaVersion;
@@ -620,13 +624,8 @@ public abstract class BaseCycloneDxMojo extends AbstractMojo {
             for (final PluginExecution execution : plugin.getExecutions()) {
                 if (execution.getGoals().contains("deploy")) {
                     final Xpp3Dom executionConf = (Xpp3Dom) execution.getConfiguration();
-                    boolean skipValue = defaultSkipValue;
-                    if (executionConf != null) {
-                        Xpp3Dom target = executionConf.getChild(parameter);
-                        if (target != null) {
-                            skipValue = Boolean.parseBoolean(target.getValue());
-                        }
-                    }
+                    final Xpp3Dom target = (executionConf == null) ? null : executionConf.getChild(parameter);
+                    final boolean skipValue = (target == null) ? defaultSkipValue : Boolean.parseBoolean(target.getValue());
                     if (!skipValue) {
                         return true;
                     }
diff --git a/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java b/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java
index 416596a..8573c33 100644
--- a/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java
+++ b/src/main/java/org/cyclonedx/maven/CycloneDxAggregateMojo.java
@@ -27,6 +27,7 @@ import org.cyclonedx.maven.ProjectDependenciesConverter.BomDependencies;
 import org.cyclonedx.model.Component;
 import org.cyclonedx.model.Dependency;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -117,9 +118,10 @@ public class CycloneDxAggregateMojo extends CycloneDxMojo {
         // root project: analyze and aggregate all the modules
         getLog().info((reactorProjects.size() <= 1) ? MESSAGE_RESOLVING_DEPS : MESSAGE_RESOLVING_AGGREGATED_DEPS);
 
+        final List<String> excludedProjects = new ArrayList<>();
         for (final MavenProject mavenProject : reactorProjects) {
             if (shouldExclude(mavenProject)) {
-                getLog().info("Excluding " + mavenProject.getArtifactId());
+            	excludedProjects.add(mavenProject.getArtifactId());
                 continue;
             }
 
@@ -135,6 +137,8 @@ public class CycloneDxAggregateMojo extends CycloneDxMojo {
             projectDependencies.forEach(dependencies::putIfAbsent);
         }
 
+        excludedProjects.stream().sorted(String.CASE_INSENSITIVE_ORDER).forEach(excluded -> getLog().info("Excluding " + excluded));
+
         addMavenProjectsAsParentDependencies(reactorProjects, dependencies);
 
         return "makeAggregateBom";
diff --git a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java
index 7799f55..ca659c4 100644
--- a/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java
+++ b/src/main/java/org/cyclonedx/maven/DefaultModelConverter.java
@@ -32,7 +32,7 @@ import org.apache.maven.project.ProjectBuilder;
 import org.apache.maven.project.ProjectBuildingException;
 import org.apache.maven.project.ProjectBuildingResult;
 import org.apache.maven.repository.RepositorySystem;
-import org.cyclonedx.CycloneDxSchema;
+import org.cyclonedx.Version;
 import org.cyclonedx.model.Component;
 import org.cyclonedx.model.ExternalReference;
 import org.cyclonedx.model.Hash;
@@ -60,6 +60,8 @@ import java.util.List;
 import java.util.Properties;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
+import org.apache.maven.model.Plugin;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
 
 @Singleton
 @Named
@@ -156,28 +158,34 @@ public class DefaultModelConverter implements ModelConverter {
     }
 
     @Override
-    public Component convertMavenDependency(Artifact artifact, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText) {
+    public Component convertMavenDependency(Artifact artifact, Version schemaVersion, boolean includeLicenseText) {
         final Component component = new Component();
         component.setGroup(artifact.getGroupId());
         component.setName(artifact.getArtifactId());
         component.setVersion(artifact.getBaseVersion());
         component.setType(Component.Type.LIBRARY);
+         
         try {
             logger.debug(BaseCycloneDxMojo.MESSAGE_CALCULATING_HASHES);
             component.setHashes(BomUtils.calculateHashes(artifact.getFile(), schemaVersion));
         } catch (IOException e) {
             logger.error("Error encountered calculating hashes", e);
         }
-        if (CycloneDxSchema.Version.VERSION_10 == schemaVersion) {
+        if (Version.VERSION_10 == schemaVersion) {
             component.setModified(isModified(artifact));
         }
         component.setPurl(generatePackageUrl(artifact));
-        if (CycloneDxSchema.Version.VERSION_10 != schemaVersion) {
+        if (Version.VERSION_10 != schemaVersion) {
             component.setBomRef(component.getPurl());
         }
         try {
             final MavenProject project = getEffectiveMavenProject(artifact);
+            
             if (project != null) {
+                String projectType = getPluginConfiguration(project, BaseCycloneDxMojo.PROJECT_TYPE);
+                if (projectType != null) {
+                    component.setType(resolveProjectType(projectType));
+                }
                 extractComponentMetadata(project, component, schemaVersion, includeLicenseText);
             }
         } catch (ProjectBuildingException e) {
@@ -191,6 +199,13 @@ public class DefaultModelConverter implements ModelConverter {
 
     }
 
+    public String getPluginConfiguration(MavenProject project, String property) {
+        Plugin plugin = project.getPlugin(BaseCycloneDxMojo.CYCLONEDX_PLUGIN_KEY);
+        Xpp3Dom configuration = (plugin == null) ? null : (Xpp3Dom) plugin.getConfiguration();
+        Xpp3Dom value = (configuration == null) ? null : configuration.getChild(property);
+        return (value == null) ? null : value.getValue();
+    }
+
     private static void setExternalReferences(Component component, ExternalReference[] externalReferences) {
         if (externalReferences == null || externalReferences.length == 0) {
             return;
@@ -210,7 +225,7 @@ public class DefaultModelConverter implements ModelConverter {
      * @param project the project to extract data from
      * @param component the component to add data to
      */
-    private void extractComponentMetadata(MavenProject project, Component component, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText) {
+    private void extractComponentMetadata(MavenProject project, Component component, Version schemaVersion, boolean includeLicenseText) {
         if (component.getPublisher() == null) {
             // If we don't already have publisher information, retrieve it.
             if (project.getOrganization() != null) {
@@ -222,12 +237,12 @@ public class DefaultModelConverter implements ModelConverter {
             component.setDescription(project.getDescription());
         }
         if (component.getLicenseChoice() == null || component.getLicenseChoice().getLicenses() == null || component.getLicenseChoice().getLicenses().isEmpty()) {
-            // If we don't already have license information, retrieve it.
-            if (project.getLicenses() != null) {
+            // If we don't already have license information, retrieve it, as long as it is not empty.
+            if (project.getLicenses() != null && project.getLicenses().stream().anyMatch(l -> !isLicenseBlank(l))) {
                 component.setLicenseChoice(resolveMavenLicenses(project.getLicenses(), schemaVersion, includeLicenseText));
             }
         }
-        if (CycloneDxSchema.Version.VERSION_10 != schemaVersion) {
+        if (Version.VERSION_10 != schemaVersion) {
             addExternalReference(ExternalReference.Type.WEBSITE, project.getUrl(), component);
             if (project.getCiManagement() != null) {
                 addExternalReference(ExternalReference.Type.BUILD_SYSTEM, project.getCiManagement().getUrl(), component);
@@ -272,7 +287,7 @@ public class DefaultModelConverter implements ModelConverter {
     }
 
     private void addExternalReference(final ExternalReference.Type referenceType, final String url, final Component component) {
-        if (isURLBlank(url) || doesComponentHaveExternalReference(component, referenceType)) {
+        if (isBlank(url) || doesComponentHaveExternalReference(component, referenceType)) {
             return;
         }
         try {
@@ -297,7 +312,7 @@ public class DefaultModelConverter implements ModelConverter {
         return false;
     }
 
-    private LicenseChoice resolveMavenLicenses(final List<org.apache.maven.model.License> projectLicenses, final CycloneDxSchema.Version schemaVersion, boolean includeLicenseText) {
+    private LicenseChoice resolveMavenLicenses(final List<org.apache.maven.model.License> projectLicenses, final Version schemaVersion, boolean includeLicenseText) {
         final LicenseChoice licenseChoice = new LicenseChoice();
         for (org.apache.maven.model.License artifactLicense : projectLicenses) {
             boolean resolved = false;
@@ -328,14 +343,13 @@ public class DefaultModelConverter implements ModelConverter {
         return licenseChoice;
     }
 
-    private boolean resolveLicenseInfo(final LicenseChoice licenseChoice, final LicenseChoice licenseChoiceToResolve, final CycloneDxSchema.Version schemaVersion)
-    {
+    private boolean resolveLicenseInfo(final LicenseChoice licenseChoice, final LicenseChoice licenseChoiceToResolve, final Version schemaVersion) {
         if (licenseChoiceToResolve != null) {
             if (licenseChoiceToResolve.getLicenses() != null && !licenseChoiceToResolve.getLicenses().isEmpty()) {
                 licenseChoice.addLicense(licenseChoiceToResolve.getLicenses().get(0));
                 return true;
             }
-            else if (licenseChoiceToResolve.getExpression() != null && CycloneDxSchema.Version.VERSION_10 != schemaVersion) {
+            else if (licenseChoiceToResolve.getExpression() != null && Version.VERSION_10 != schemaVersion) {
                 licenseChoice.setExpression(licenseChoiceToResolve.getExpression());
                 return true;
             }
@@ -344,7 +358,7 @@ public class DefaultModelConverter implements ModelConverter {
     }
 
     @Override
-    public Metadata convertMavenProject(final MavenProject project, String projectType, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText, ExternalReference[] externalReferences) {
+    public Metadata convertMavenProject(final MavenProject project, String projectType, Version schemaVersion, boolean includeLicenseText, ExternalReference[] externalReferences) {
         final Metadata metadata = new Metadata();
 
         // prepare properties and hash values from the current mojo
@@ -361,7 +375,7 @@ public class DefaultModelConverter implements ModelConverter {
                 logger.warn("Unable to calculate hashes of self", e);
             }
         }
-        if (schemaVersion.compareTo(CycloneDxSchema.Version.VERSION_15) < 0) {
+        if (schemaVersion.compareTo(Version.VERSION_15) < 0) {
             // CycloneDX up to 1.4+ use metadata.tools.tool
             final Tool tool = new Tool();
             tool.setVendor(properties.getProperty("vendor"));
@@ -422,7 +436,11 @@ public class DefaultModelConverter implements ModelConverter {
         return Component.Type.LIBRARY;
     }
 
-    private static boolean isURLBlank(String url) {
-        return url == null || url.isEmpty() || url.trim().length() == 0;
+    private static boolean isBlank(String s) {
+        return s == null || s.isEmpty() || s.trim().isEmpty();
+    }
+
+    private static boolean isLicenseBlank(org.apache.maven.model.License license) {
+        return isBlank(license.getName()) && isBlank(license.getUrl());
     }
 }
diff --git a/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java b/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java
index 3b00eda..b8a286d 100644
--- a/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java
+++ b/src/main/java/org/cyclonedx/maven/DefaultProjectDependenciesConverter.java
@@ -40,10 +40,12 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.lang.reflect.Field;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -242,6 +244,7 @@ public class DefaultProjectDependenciesConverter implements ProjectDependenciesC
 
         // Check all BOM components have an associated BOM dependency
 
+        final List<String> notDepended = new ArrayList<>();
         for (Iterator<Map.Entry<String, Component>> it = components.entrySet().iterator(); it.hasNext(); ) {
             Map.Entry<String, Component> entry = it.next();
             if (!dependencies.containsKey(entry.getKey())) {
@@ -250,10 +253,12 @@ public class DefaultProjectDependenciesConverter implements ProjectDependenciesC
                 }
                 it.remove();
             } else if (!dependsOns.contains(entry.getKey())) {
-                logger.warn("BOM dependency listed but is not depended upon: " + entry.getKey());
+                notDepended.add(entry.getKey());
             }
         }
 
+        notDepended.stream().sorted(String.CASE_INSENSITIVE_ORDER).forEach(dependency -> logger.warn("BOM dependency listed but is not depended upon: " + dependency));
+
         // include BOM main component
         Component main = metadata.getComponent();
         final String mainBomRef = main.getBomRef();
diff --git a/src/main/java/org/cyclonedx/maven/ModelConverter.java b/src/main/java/org/cyclonedx/maven/ModelConverter.java
index a1fd1c2..7aec1a0 100644
--- a/src/main/java/org/cyclonedx/maven/ModelConverter.java
+++ b/src/main/java/org/cyclonedx/maven/ModelConverter.java
@@ -20,7 +20,7 @@ package org.cyclonedx.maven;
 
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.project.MavenProject;
-import org.cyclonedx.CycloneDxSchema;
+import org.cyclonedx.Version;
 import org.cyclonedx.model.Component;
 import org.cyclonedx.model.ExternalReference;
 import org.cyclonedx.model.Metadata;
@@ -49,7 +49,7 @@ public interface ModelConverter {
      * @param includeLicenseText should license text be included in bom?
      * @return a CycloneDX component
      */
-    Component convertMavenDependency(Artifact artifact, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText);
+    Component convertMavenDependency(Artifact artifact, Version schemaVersion, boolean includeLicenseText);
 
     /**
      * Converts a MavenProject into a CycloneDX Metadata object.
@@ -61,6 +61,6 @@ public interface ModelConverter {
      * @param externalReferences the external references
      * @return a CycloneDX Metadata object
      */
-    Metadata convertMavenProject(MavenProject project, String projectType, CycloneDxSchema.Version schemaVersion, boolean includeLicenseText, ExternalReference[] externalReferences);
+    Metadata convertMavenProject(MavenProject project, String projectType, Version schemaVersion, boolean includeLicenseText, ExternalReference[] externalReferences);
 
 }
diff --git a/src/site/markdown/external-references.md b/src/site/markdown/external-references.md
index a482c0b..c4d81fa 100644
--- a/src/site/markdown/external-references.md
+++ b/src/site/markdown/external-references.md
@@ -50,6 +50,6 @@ You can add more external references the component that the BOM describes by plu
 See valid [CycloneDX external reference types][external-reference-type].
 
 [maven-model]: https://maven.apache.org/ref/current/maven-model/maven.html
-[metadata-component]: https://cyclonedx.org/docs/1.5/json/#metadata_component
-[components]: https://cyclonedx.org/docs/1.5/json/#components
-[external-reference-type]: https://cyclonedx.org/docs/1.5/json/#metadata_component_externalReferences_items_type
+[metadata-component]: https://cyclonedx.org/docs/1.6/json/#metadata_component
+[components]: https://cyclonedx.org/docs/1.6/json/#components
+[external-reference-type]: https://cyclonedx.org/docs/1.6/json/#metadata_component_externalReferences_items_type
diff --git a/src/site/site.xml b/src/site/site.xml
index 0cd83a3..c094cc2 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -3,7 +3,7 @@
     <skin>
         <groupId>org.apache.maven.skins</groupId>
         <artifactId>maven-fluido-skin</artifactId>
-        <version>1.10.0</version>
+        <version>1.12.0</version>
     </skin>
     <custom>
         <fluidoSkin>


Test patch:
diff --git a/src/test/java/org/cyclonedx/maven/Issue382Test.java b/src/test/java/org/cyclonedx/maven/Issue382Test.java
new file mode 100644
index 0000000..a88bcbb
--- /dev/null
+++ b/src/test/java/org/cyclonedx/maven/Issue382Test.java
@@ -0,0 +1,47 @@
+package org.cyclonedx.maven;
+
+import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder;
+import io.takari.maven.testing.executor.MavenVersions;
+import io.takari.maven.testing.executor.junit.MavenJUnitTestRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+import static io.takari.maven.testing.TestResources.assertFilesPresent;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Test for <a href="https://github.com/CycloneDX/cyclonedx-maven-plugin/issues/382">issue #382</a>:
+ * Plugin does not gracefully handle present, but empty license data
+ */
+@RunWith(MavenJUnitTestRunner.class)
+@MavenVersions({"3.6.3"})
+public class Issue382Test extends BaseMavenVerifier {
+
+    public Issue382Test(MavenRuntimeBuilder runtimeBuilder) throws Exception {
+        super(runtimeBuilder);
+    }
+
+    @Test
+    public void test() throws Exception {
+        File projDir = resources.getBasedir("issue-382");
+
+        verifier
+                .forProject(projDir)
+                .withCliOption("-Dcurrent.version=" + getCurrentVersion()) // inject cyclonedx-maven-plugin version
+                .withCliOption("-X") // debug
+                .withCliOption("-B")
+                .execute("clean", "verify")
+                .assertErrorFreeLog();
+
+        assertFileNotContains(projDir, "target/bom.xml", "The BOM does not conform to the CycloneDX BOM standard");
+    }
+
+    private static void assertFileNotContains(File basedir, String expectedFile, String expectedContent) throws IOException {
+        assertFilesPresent(basedir, expectedFile);
+        String bomContents = fileRead(new File(basedir, expectedFile), true);
+        assertFalse(String.format("%s contains %s", expectedFile, expectedContent), bomContents.contains(expectedContent));
+    }
+}
diff --git a/src/test/java/org/cyclonedx/maven/Issue521Test.java b/src/test/java/org/cyclonedx/maven/Issue521Test.java
new file mode 100644
index 0000000..5dae56c
--- /dev/null
+++ b/src/test/java/org/cyclonedx/maven/Issue521Test.java
@@ -0,0 +1,77 @@
+package org.cyclonedx.maven;
+
+import org.junit.runner.RunWith;
+
+
+import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder;
+import io.takari.maven.testing.executor.MavenVersions;
+import io.takari.maven.testing.executor.junit.MavenJUnitTestRunner;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import static org.cyclonedx.maven.TestUtils.readXML;
+
+
+import java.io.File;
+import java.io.IOException;
+
+
+
+import org.junit.Test;
+
+@RunWith(MavenJUnitTestRunner.class)
+@MavenVersions({"3.6.3"})
+public class Issue521Test extends BaseMavenVerifier {
+    public Issue521Test(MavenRuntimeBuilder runtimeBuilder) throws Exception {
+        super(runtimeBuilder);
+    }
+
+    @Test
+    public void testBomJsonContent() throws Exception {
+        File projDir = cleanAndBuild("issue-521", null);
+        checkBomXml(projDir, "target/bom.xml", "issue-521-app", "application");
+        checkBomJson(projDir, "target/bom.json", "issue-521-app", "application");
+    }
+
+    private static void assertFileExists(File file) {
+        assertTrue(file.exists(), String.format("File %s should exist", file));
+    }
+
+    private void checkBomJson(File basedir, String filePath, String expectedName, String expectedType) throws IOException {
+        File bomJsonFile = new File(basedir, filePath);
+        assertFileExists(bomJsonFile);
+
+        // Leggi il contenuto del file bom.json e verifica la presenza dei campi desiderati
+        String bomContents = fileRead(bomJsonFile, true);
+        assertTrue(bomContents.contains("\"name\" : \"" + expectedName + "\""), 
+            String.format("bom.json should contain a module with name '%s'", expectedName));
+        assertTrue(bomContents.contains("\"type\" : \"" + expectedType + "\""), 
+            String.format("bom.json should contain a module with type '%s'", expectedType));
+    }
+
+    private void checkBomXml(File basedir, String filePath, String expectedName, String expectedType) throws Exception {
+        File bomXmlFile = new File(basedir, filePath);
+        assertFileExists(bomXmlFile);
+
+        final Document bom = readXML(bomXmlFile);
+
+        final NodeList componentsList = bom.getElementsByTagName("component");
+        assertTrue(componentsList.getLength() > 0, "Expected at least one component element");
+
+        boolean found=false;
+        for (int i = 0; i < componentsList.getLength(); i++) {
+            Element component = (Element) componentsList.item(i);
+            String name = component.getElementsByTagName("name").item(0).getTextContent();
+            String type = component.getAttribute("type");
+
+            if (expectedName.equals(name) && expectedType.equals(type)) {
+                found = true;
+                break;
+            }
+        }
+
+        assertTrue(found, "Expected to find a component with name 'module1' and type 'application'");
+    }
+}
diff --git a/src/test/resources/issue-382/pom.xml b/src/test/resources/issue-382/pom.xml
new file mode 100644
index 0000000..15d900f
--- /dev/null
+++ b/src/test/resources/issue-382/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.example</groupId>
+    <artifactId>issue-382</artifactId>
+    <packaging>jar</packaging>
+    <version>1.0.0</version>
+
+    <name>Issue-64</name>
+
+    <licenses>
+        <license>
+            <name/>
+            <url/>
+            <distribution/>
+        </license>
+    </licenses>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency> <!-- has empty license information -->
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-ram</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.cyclonedx</groupId>
+                <artifactId>cyclonedx-maven-plugin</artifactId>
+                <version>${current.version}</version>
+                <executions>
+                    <execution>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>makeAggregateBom</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <projectType>library</projectType>
+                    <schemaVersion>1.6</schemaVersion>
+                    <includeLicenseText>true</includeLicenseText>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/src/test/resources/issue-521/app/pom.xml b/src/test/resources/issue-521/app/pom.xml
new file mode 100644
index 0000000..381bfd6
--- /dev/null
+++ b/src/test/resources/issue-521/app/pom.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.example</groupId>
+        <artifactId>issue-521</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>issue-521-app</artifactId>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.cyclonedx</groupId>
+                <artifactId>cyclonedx-maven-plugin</artifactId>
+                <version>${current.version}</version>
+                <configuration>
+                    <projectType>application</projectType>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/src/test/resources/issue-521/lib/pom.xml b/src/test/resources/issue-521/lib/pom.xml
new file mode 100644
index 0000000..604d5e6
--- /dev/null
+++ b/src/test/resources/issue-521/lib/pom.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.example</groupId>
+        <artifactId>issue-521</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>issue-521-lib</artifactId>
+</project>
diff --git a/src/test/resources/issue-521/pom.xml b/src/test/resources/issue-521/pom.xml
new file mode 100644
index 0000000..0cfbe81
--- /dev/null
+++ b/src/test/resources/issue-521/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.example</groupId>
+    <artifactId>issue-521</artifactId>
+    <packaging>pom</packaging>
+    <version>${revision}</version>
+
+    <licenses>
+        <license>
+            <name>Apache-2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+        </license>
+    </licenses>
+
+    <modules>
+        <module>app</module>
+        <module>lib</module>
+    </modules>
+
+    <properties>
+        <revision>1.0.0</revision>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.cyclonedx</groupId>
+                <artifactId>cyclonedx-maven-plugin</artifactId>
+                <version>${current.version}</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>makeAggregateBom</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file