Repository: porscheinformatik/sonarqube-licensecheck
Pull request #177 (2020-07-07 08:48:23)

Added Gradle Support using JK1 plugin
Hi Team, We have implemented Gradle support using JK1 plugin.

https://github.com/jk1/Gradle-License-Report

derkoe: There is still one issue: this PR changes the detection of the scanners.

For example if you have this structure
```
root
- pom.xml
- subproject1
  - package.json
```
then the `PackageJsonDependencyScanner` is not invoked since the root has no `package.json`

The current version is handling case. Maybe always call all scanners and skip the current if the wanted files (`pom.xml`, `package.json`, `build/reports/dependency-license/license-details.json` are not present.
chakrapanich: > There is still one issue: this PR changes the detection of the scanners.
> 
> For example if you have this structure
> 
> ```
> root
> - pom.xml
> - subproject1
>   - package.json
> ```
> 
> then the `PackageJsonDependencyScanner` is not invoked since the root has no `package.json`
> 
> The current version is handling case. Maybe always call all scanners and skip the current if the wanted files (`pom.xml`, `package.json`, `build/reports/dependency-license/license-details.json` are not present.

yes, changed scanner resolve method.

Patch:
diff --git a/README.md b/README.md
index dad969c..bd7d7cf 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,60 @@ After booting the SonarQube Server with the License-Check Plugin two new options
 ![License
 configuration](docs/licensecheck_configuration.jpg)
 
+### Maven
+
+Maven works if your project/module has a `pom.xml` on its root level (running with Maven, Gradle or SonarScanner).
+
+### NPM
+
+NPM works if your project/module has a `package.json` on its root level (running with Maven, Gradle or SonarScanner).
+
+### Gradle
+
+Gradle project should use JK1 plugin https://github.com/jk1/Gradle-License-Report
+
+Note: Please check above link for instructions or follow as mentioned below
+
+**Step1:** Update `build.gradle` file with following code for using JK1 plugin
+
+    import com.github.jk1.license.filter.LicenseBundleNormalizer
+    import com.github.jk1.license.render.JsonReportRenderer
+
+    plugins {
+      id 'com.github.jk1.dependency-license-report' version '1.13'
+    }
+
+    licenseReport {
+        allowedLicensesFile = new File("$projectDir/src/main/resources/licenses/allowed-licenses.json")
+        renderers = new JsonReportRenderer('license-details.json', false)
+        filters = [new LicenseBundleNormalizer()]
+    }
+
+**Step 2:** Update `build.gradle` file with following code for using SonarQube plugin
+
+    plugins {
+        id 'org.sonarqube' version "3.0"
+    }
+
+    jar {
+        enabled = true
+    }
+
+    sonarqube {
+        properties {
+            property "sonar.host.url", "http://localhost:9000"
+        }
+    }
+
+**Step 3:** run following command  to generate your report `license-details.json` in  `build/reports/dependency-license`
+
+    > gradle generateLicenseReport
+
+**Step 4:** run following command for SonarQube
+
+    > gradle sonarqube
+
+
 ## Features
 
 ### Analysis
diff --git a/pom.xml b/pom.xml
index b0b72e0..7132f41 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
   <groupId>at.porscheinformatik.sonarqube.licensecheck</groupId>
   <artifactId>sonarqube-licensecheck-plugin</artifactId>
-  <version>4.0.0-SNAPSHOT</version>
+  <version>4.0.0-SNAPSHOT-gradle-jk1</version>
   <name>SonarQube License Check Plugin</name>
   <packaging>sonar-plugin</packaging>
 
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java
index 2ac7205..010425a 100644
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java
+++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/LicenseCheckSensor.java
@@ -6,6 +6,7 @@ import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 
+import at.porscheinformatik.sonarqube.licensecheck.gradle.GradleDependencyScanner;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputModule;
@@ -26,7 +27,7 @@ import at.porscheinformatik.sonarqube.licensecheck.npm.PackageJsonDependencyScan
 public class LicenseCheckSensor implements Sensor
 {
     private static final Logger LOGGER = Loggers.get(LicenseCheckSensor.class);
-    private final static Set<License> AGGREGATED_LICENSES = newSetFromMap(new ConcurrentHashMap<License, Boolean>());
+    private final static Set<License> AGGREGATED_LICENSES = newSetFromMap(new ConcurrentHashMap<>());
     private final static Set<Dependency> AGGREGATED_DEPENDENCIES = newSetFromMap(new ConcurrentHashMap<>());
     private final FileSystem fs;
     private final Configuration configuration;
@@ -42,12 +43,13 @@ public class LicenseCheckSensor implements Sensor
         this.scanners = new Scanner[]{
             new PackageJsonDependencyScanner(
                 configuration.getBoolean(LicenseCheckPropertyKeys.NPM_RESOLVE_TRANSITIVE_DEPS).orElse(false)),
-            new MavenDependencyScanner(mavenLicenseService, mavenDependencyService)};
+            new MavenDependencyScanner(mavenLicenseService, mavenDependencyService),
+            new GradleDependencyScanner(mavenLicenseService)};
     }
 
     private static void saveDependencies(SensorContext sensorContext, Set<Dependency> dependencies)
     {
-        LOGGER.debug("Saving depenencies for module {}: {}", sensorContext.module(), dependencies);
+        LOGGER.debug("Saving dependencies for module {}: {}", sensorContext.module(), dependencies);
 
         if (!dependencies.isEmpty())
         {
@@ -97,10 +99,10 @@ public class LicenseCheckSensor implements Sensor
         {
             dependencies.addAll(scanner.scan(fs.baseDir()));
         }
-
         ProjectDefinition project =
             LicenseCheckPlugin.getRootProject(((DefaultInputModule) context.module()).definition());
         Set<Dependency> validatedDependencies = validateLicenses.validateLicenses(dependencies, context);
+
         Set<License> usedLicenses = validateLicenses.getUsedLicenses(validatedDependencies, project);
 
         AGGREGATED_LICENSES.addAll(usedLicenses);
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java
new file mode 100644
index 0000000..eac5339
--- /dev/null
+++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScanner.java
@@ -0,0 +1,136 @@
+package at.porscheinformatik.sonarqube.licensecheck.gradle;
+
+import at.porscheinformatik.sonarqube.licensecheck.Dependency;
+import at.porscheinformatik.sonarqube.licensecheck.interfaces.Scanner;
+import at.porscheinformatik.sonarqube.licensecheck.mavenlicense.MavenLicenseService;
+
+import org.codehaus.plexus.util.StringUtils;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+
+import java.io.*;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class GradleDependencyScanner implements Scanner
+{
+    private static final Logger LOGGER = Loggers.get(GradleDependencyScanner.class);
+    private final MavenLicenseService mavenLicenseService;
+
+    public GradleDependencyScanner(MavenLicenseService mavenLicenseService)
+    {
+        this.mavenLicenseService = mavenLicenseService;
+    }
+
+    @Override
+    public Set<Dependency> scan(File moduleDir)
+    {
+        Map<Pattern, String> defaultLicenseMap = mavenLicenseService.getLicenseMap();
+
+        File licenseDetailsJsonFile = new File(moduleDir, "build" + File.separator + "reports" + File.separator
+            + "dependency-license" + File.separator + "license-details.json");
+
+        if (!licenseDetailsJsonFile.exists())
+        {
+            LOGGER.info("No license-details.json file found in {} - skipping Gradle dependency scan",
+                licenseDetailsJsonFile.getPath());
+            return Collections.emptySet();
+        }
+
+        return readLicenseDetailsJson(licenseDetailsJsonFile)
+            .stream()
+            .map(d -> mapMavenDependencyToLicense(defaultLicenseMap, d))
+            .collect(Collectors.toSet());
+    }
+
+    private Set<Dependency> readLicenseDetailsJson(File licenseDetailsJsonFile)
+    {
+        Set<Dependency> dependencySet = new HashSet<>();
+        try (InputStream fis = new FileInputStream(licenseDetailsJsonFile);
+            JsonReader jsonReader = Json.createReader(fis))
+        {
+            JsonArray arr = jsonReader.readObject().getJsonArray("dependencies");
+            prepareDependencySet(dependencySet, arr);
+            return dependencySet;
+        }
+        catch (IOException e)
+        {
+            LOGGER.error("Problems reading Gradle license file {}: {}",
+                licenseDetailsJsonFile.getPath(), e.getMessage());
+        }
+        return dependencySet;
+    }
+
+    private void prepareDependencySet(Set<Dependency> dependencySet, JsonArray arr)
+    {
+        for (javax.json.JsonValue entry : arr)
+        {
+            JsonObject jsonDepObj = entry.asJsonObject();
+            JsonArray arrModuleUrls = jsonDepObj.getJsonArray("moduleUrls");
+            String moduleLicense = getModuleLicenseFromJsonObject(jsonDepObj);
+            String moduleLicenseUrl = null;
+            if (arrModuleUrls != null)
+            {
+                moduleLicenseUrl = arrModuleUrls.getString(0, null);
+            }
+            Dependency dep = new Dependency(jsonDepObj.getString("moduleName", null),
+                jsonDepObj.getString("moduleVersion", null), moduleLicense);
+            dep.setPomPath(moduleLicenseUrl);
+            dependencySet.add(dep);
+        }
+    }
+
+    private String getModuleLicenseFromJsonObject(JsonObject jsonDepObj)
+    {
+        String moduleLicense = null;
+        JsonArray arrModuleLicenses = jsonDepObj.getJsonArray("moduleLicenses");
+        if (arrModuleLicenses != null)
+        {
+            moduleLicense = getModuleLicense(arrModuleLicenses);
+        }
+        return moduleLicense;
+    }
+
+    private String getModuleLicense(JsonArray arrModuleLicenses)
+    {
+        String moduleLicense = null;
+        JsonObject firstJsonObj = arrModuleLicenses.getJsonObject(0);
+        if (firstJsonObj != null)
+        {
+            moduleLicense = firstJsonObj.getString("moduleLicense", null);
+            if (moduleLicense == null)
+            {
+                moduleLicense = firstJsonObj.getString("moduleLicenseUrl", null);
+            }
+        }
+        return moduleLicense;
+    }
+
+    private Dependency mapMavenDependencyToLicense(Map<Pattern, String> defaultLicenseMap, Dependency dependency)
+    {
+        if (StringUtils.isBlank(dependency.getLicense()))
+        {
+            LOGGER.error(" License not found for Dependency {}", dependency);
+            return dependency;
+        }
+
+        for (Map.Entry<Pattern, String> allowedDependency : defaultLicenseMap.entrySet())
+        {
+            if (allowedDependency.getKey().matcher(dependency.getLicense()).matches())
+            {
+                dependency.setLicense(allowedDependency.getValue());
+                break;
+            }
+        }
+        return dependency;
+    }
+}
diff --git a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/npm/PackageJsonDependencyScanner.java b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/npm/PackageJsonDependencyScanner.java
index 0607193..342547a 100644
--- a/src/main/java/at/porscheinformatik/sonarqube/licensecheck/npm/PackageJsonDependencyScanner.java
+++ b/src/main/java/at/porscheinformatik/sonarqube/licensecheck/npm/PackageJsonDependencyScanner.java
@@ -99,18 +99,18 @@ public class PackageJsonDependencyScanner implements Scanner
                     String license = "";
                     if (packageJson.containsKey("license"))
                     {
-                        license = packageJson.getString("license");
+                        license = packageJson.getString("license", null);
                     }
                     else if (packageJson.containsKey("licenses"))
                     {
                         JsonArray licenses = packageJson.getJsonArray("licenses");
                         if (licenses.size() > 0)
                         {
-                            license = licenses.getJsonObject(0).getString("type");
+                            license = licenses.getJsonObject(0).getString("type", null);
                         }
                     }
 
-                    dependencies.add(new Dependency(packageName, packageJson.getString("version"), license));
+                    dependencies.add(new Dependency(packageName, packageJson.getString("version", null), license));
 
                     if (resolveTransitiveDeps)
                     {


Test patch:
diff --git a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScannerTest.java b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScannerTest.java
new file mode 100644
index 0000000..ed57d90
--- /dev/null
+++ b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/gradle/GradleDependencyScannerTest.java
@@ -0,0 +1,49 @@
+package at.porscheinformatik.sonarqube.licensecheck.gradle;
+
+import at.porscheinformatik.sonarqube.licensecheck.Dependency;
+import at.porscheinformatik.sonarqube.licensecheck.mavenlicense.MavenLicenseService;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+public class GradleDependencyScannerTest
+{
+    private MavenLicenseService mockLicenseService()
+    {
+        Map<Pattern, String> licenseMap = new HashMap<>();
+        licenseMap.put(Pattern.compile(".*Apache.*2.*"), "Apache-2.0");
+        MavenLicenseService licenseService = Mockito.mock(MavenLicenseService.class);
+        when(licenseService.getLicenseMap()).thenReturn(licenseMap);
+        return licenseService;
+    }
+
+    @Test
+    public void testScannerWithMissingJsonFile()
+    {
+        GradleDependencyScanner scanner = new GradleDependencyScanner(mockLicenseService());
+        Set<Dependency> dependencies = scanner.scan(new File("/abc"));
+        assertEquals(0, dependencies.size());
+    }
+
+    @Test
+    public void testScanner()
+    {
+        GradleDependencyScanner scanner = new GradleDependencyScanner(mockLicenseService());
+        Path resourceDirectory = Paths.get("src", "test", "resources");
+        String absolutePath = resourceDirectory.toFile().getAbsolutePath();
+
+        Set<Dependency> dependencies = scanner.scan(new File(absolutePath));
+        assertEquals(43, dependencies.size());
+    }
+}
diff --git a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/MavenDependencyScannerTest.java b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/MavenDependencyScannerTest.java
index 0020470..66a0be9 100644
--- a/src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/MavenDependencyScannerTest.java
+++ b/src/test/java/at/porscheinformatik/sonarqube/licensecheck/maven/MavenDependencyScannerTest.java
@@ -132,7 +132,6 @@ public class MavenDependencyScannerTest
             Scanner scanner = new MavenDependencyScanner(mockLicenseService(), Mockito.mock(MavenDependencyService.class));
             
             Set<Dependency> dependencies = scanner.scan(new File("."));
-
             assertThat(dependencies.size(), is(0));
         }
         finally
diff --git a/src/test/resources/build/reports/dependency-license/license-details.json b/src/test/resources/build/reports/dependency-license/license-details.json
new file mode 100644
index 0000000..8df2eaf
--- /dev/null
+++ b/src/test/resources/build/reports/dependency-license/license-details.json
@@ -0,0 +1,745 @@
+{
+  "dependencies": [
+    {
+      "moduleName": "ch.qos.logback:logback-classic",
+      "moduleVersion": "1.2.3",
+      "moduleUrls": [
+        "http://www.qos.ch"
+      ]
+    },
+    {
+      "moduleName": "ch.qos.logback:logback-core",
+      "moduleVersion": "1.2.3",
+      "moduleUrls": [
+        "http://www.qos.ch"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": null,
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-v10.html, http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"
+        },
+        {
+          "moduleLicense": "Eclipse Public License - v 1.0",
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-v10.html"
+        },
+        {
+          "moduleLicense": "GNU Lesser General Public License",
+          "moduleLicenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"
+        }
+      ]
+    },
+    {
+      "moduleName": "com.fasterxml.jackson.core:jackson-annotations",
+      "moduleVersion": "2.10.4",
+      "moduleUrls": [
+        "http://github.com/FasterXML/jackson"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "The Apache Software License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "com.fasterxml.jackson.core:jackson-core",
+      "moduleVersion": "2.10.4",
+      "moduleUrls": [
+        "https://github.com/FasterXML/jackson-core"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "The Apache Software License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "com.fasterxml.jackson.core:jackson-databind",
+      "moduleVersion": "2.10.4",
+      "moduleUrls": [
+        "http://github.com/FasterXML/jackson"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "The Apache Software License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jdk8",
+      "moduleVersion": "2.10.4",
+      "moduleUrls": [
+        "https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "The Apache Software License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jsr310",
+      "moduleVersion": "2.10.4",
+      "moduleUrls": [
+        "https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "The Apache Software License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "com.fasterxml.jackson.module:jackson-module-parameter-names",
+      "moduleVersion": "2.10.4",
+      "moduleUrls": [
+        "https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "The Apache Software License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "com.fasterxml:classmate",
+      "moduleVersion": "1.5.1",
+      "moduleUrls": [
+        "https://github.com/FasterXML/java-classmate"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
+        },
+        {
+          "moduleLicense": "The Apache Software License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "jakarta.annotation:jakarta.annotation-api",
+      "moduleVersion": "1.3.5",
+      "moduleUrls": [
+        "https://projects.eclipse.org/projects/ee4j.ca",
+        "https://www.eclipse.org"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": null,
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "EPL 2.0",
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0"
+        },
+        {
+          "moduleLicense": "Eclipse Public License v. 2.0",
+          "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt"
+        },
+        {
+          "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "GPL2 w/ CPE",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        }
+      ]
+    },
+    {
+      "moduleName": "jakarta.validation:jakarta.validation-api",
+      "moduleVersion": "2.0.2",
+      "moduleUrls": [
+        "https://beanvalidation.org",
+        "https://www.eclipse.org"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        },
+        {
+          "moduleLicense": "Eclipse Public License v. 2.0",
+          "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt"
+        },
+        {
+          "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        }
+      ]
+    },
+    {
+      "moduleName": "jakarta.ws.rs:jakarta.ws.rs-api",
+      "moduleVersion": "2.1.6",
+      "moduleUrls": [
+        "https://github.com/eclipse-ee4j/jaxrs-api",
+        "https://www.eclipse.org/org/foundation/"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": null,
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "EPL 2.0",
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0"
+        },
+        {
+          "moduleLicense": "GPL2 w/ CPE",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.apache.logging.log4j:log4j-api",
+      "moduleVersion": "2.12.1",
+      "moduleUrls": [
+        "https://www.apache.org/"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.apache.logging.log4j:log4j-to-slf4j",
+      "moduleVersion": "2.12.1",
+      "moduleUrls": [
+        "https://www.apache.org/"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.apache.tomcat.embed:tomcat-embed-core",
+      "moduleVersion": "9.0.36",
+      "moduleUrls": [
+        "https://tomcat.apache.org/"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        },
+        {
+          "moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0",
+          "moduleLicenseUrl": "https://opensource.org/licenses/CDDL-1.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.apache.tomcat.embed:tomcat-embed-el",
+      "moduleVersion": "9.0.36",
+      "moduleUrls": [
+        "https://tomcat.apache.org/"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.apache.tomcat.embed:tomcat-embed-websocket",
+      "moduleVersion": "9.0.36",
+      "moduleUrls": [
+        "https://tomcat.apache.org/"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.glassfish.hk2.external:jakarta.inject",
+      "moduleVersion": "2.6.1",
+      "moduleUrls": [
+        "http://www.oracle.com"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": null,
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "EPL 2.0",
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0"
+        },
+        {
+          "moduleLicense": "Eclipse Public License v. 2.0",
+          "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt"
+        },
+        {
+          "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "GPL2 w/ CPE",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.glassfish.hk2:osgi-resource-locator",
+      "moduleVersion": "1.0.3",
+      "moduleUrls": [
+        "https://www.eclipse.org"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": null,
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "EPL 2.0",
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0"
+        },
+        {
+          "moduleLicense": "Eclipse Public License v. 2.0",
+          "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt"
+        },
+        {
+          "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "GPL2 w/ CPE",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.glassfish.jersey.core:jersey-client",
+      "moduleVersion": "2.29.1",
+      "moduleUrls": [
+        "https://www.eclipse.org/org/foundation/"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": null,
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html, http://www.eclipse.org/org/documents/edl-v10.php, https://opensource.org/licenses/BSD-2-Clause, http://www.apache.org/licenses/LICENSE-2.0.html, https://creativecommons.org/publicdomain/zero/1.0/, http://asm.objectweb.org/license.html, jquery.org/license, http://www.opensource.org/licenses/mit-license.php, https://www.w3.org/Consortium/Legal/copyright-documents-19990405"
+        },
+        {
+          "moduleLicense": "Apache License, 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html"
+        },
+        {
+          "moduleLicense": "BSD 2-Clause",
+          "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause"
+        },
+        {
+          "moduleLicense": "EDL 1.0",
+          "moduleLicenseUrl": "http://www.eclipse.org/org/documents/edl-v10.php"
+        },
+        {
+          "moduleLicense": "EPL 2.0",
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0"
+        },
+        {
+          "moduleLicense": "Eclipse Public License v. 2.0",
+          "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt"
+        },
+        {
+          "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "GPL2 w/ CPE",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "MIT license",
+          "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php"
+        },
+        {
+          "moduleLicense": "Modified BSD",
+          "moduleLicenseUrl": "http://asm.objectweb.org/license.html"
+        },
+        {
+          "moduleLicense": "Public Domain",
+          "moduleLicenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/"
+        },
+        {
+          "moduleLicense": "W3C license",
+          "moduleLicenseUrl": "https://www.w3.org/Consortium/Legal/copyright-documents-19990405"
+        },
+        {
+          "moduleLicense": "jQuery license",
+          "moduleLicenseUrl": "jquery.org/license"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.glassfish.jersey.core:jersey-common",
+      "moduleVersion": "2.29.1",
+      "moduleUrls": [
+        "https://www.eclipse.org/org/foundation/"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": null,
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0, https://www.gnu.org/software/classpath/license.html, http://www.apache.org/licenses/LICENSE-2.0.html, https://creativecommons.org/publicdomain/zero/1.0/"
+        },
+        {
+          "moduleLicense": "Apache License, 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html"
+        },
+        {
+          "moduleLicense": "BSD 2-Clause",
+          "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause"
+        },
+        {
+          "moduleLicense": "EDL 1.0",
+          "moduleLicenseUrl": "http://www.eclipse.org/org/documents/edl-v10.php"
+        },
+        {
+          "moduleLicense": "EPL 2.0",
+          "moduleLicenseUrl": "http://www.eclipse.org/legal/epl-2.0"
+        },
+        {
+          "moduleLicense": "Eclipse Public License v. 2.0",
+          "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt"
+        },
+        {
+          "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "GPL2 w/ CPE",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "MIT license",
+          "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php"
+        },
+        {
+          "moduleLicense": "Modified BSD",
+          "moduleLicenseUrl": "http://asm.objectweb.org/license.html"
+        },
+        {
+          "moduleLicense": "Public Domain",
+          "moduleLicenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/"
+        },
+        {
+          "moduleLicense": "The GNU General Public License (GPL), Version 2, With Classpath Exception",
+          "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html"
+        },
+        {
+          "moduleLicense": "W3C license",
+          "moduleLicenseUrl": "https://www.w3.org/Consortium/Legal/copyright-documents-19990405"
+        },
+        {
+          "moduleLicense": "jQuery license",
+          "moduleLicenseUrl": "jquery.org/license"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.hibernate.validator:hibernate-validator",
+      "moduleVersion": "6.0.20.Final",
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.jboss.logging:jboss-logging",
+      "moduleVersion": "3.4.1.Final",
+      "moduleUrls": [
+        "http://www.jboss.org"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        },
+        {
+          "moduleLicense": "Apache License, version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        },
+        {
+          "moduleLicense": "Public Domain",
+          "moduleLicenseUrl": "http://repository.jboss.org/licenses/cc0-1.0.txt"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.slf4j:jul-to-slf4j",
+      "moduleVersion": "1.7.30",
+      "moduleUrls": [
+        "http://www.slf4j.org"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "MIT License",
+          "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.slf4j:slf4j-api",
+      "moduleVersion": "1.7.30",
+      "moduleUrls": [
+        "http://www.slf4j.org"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "MIT License",
+          "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework.boot:spring-boot",
+      "moduleVersion": "2.2.8.RELEASE",
+      "moduleUrls": [
+        "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework.boot:spring-boot-autoconfigure",
+      "moduleVersion": "2.2.8.RELEASE",
+      "moduleUrls": [
+        "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-autoconfigure"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework.boot:spring-boot-devtools",
+      "moduleVersion": "2.2.8.RELEASE",
+      "moduleUrls": [
+        "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-devtools"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework.boot:spring-boot-starter",
+      "moduleVersion": "2.2.8.RELEASE",
+      "moduleUrls": [
+        "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework.boot:spring-boot-starter-json",
+      "moduleVersion": "2.2.8.RELEASE",
+      "moduleUrls": [
+        "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-json"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework.boot:spring-boot-starter-logging",
+      "moduleVersion": "2.2.8.RELEASE",
+      "moduleUrls": [
+        "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-logging"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework.boot:spring-boot-starter-tomcat",
+      "moduleVersion": "2.2.8.RELEASE",
+      "moduleUrls": [
+        "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-tomcat"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework.boot:spring-boot-starter-validation",
+      "moduleVersion": "2.2.8.RELEASE",
+      "moduleUrls": [
+        "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-validation"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework.boot:spring-boot-starter-web",
+      "moduleVersion": "2.2.8.RELEASE",
+      "moduleUrls": [
+        "https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework:spring-aop",
+      "moduleVersion": "5.2.7.RELEASE",
+      "moduleUrls": [
+        "https://github.com/spring-projects/spring-framework"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework:spring-beans",
+      "moduleVersion": "5.2.7.RELEASE",
+      "moduleUrls": [
+        "https://github.com/spring-projects/spring-framework"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework:spring-context",
+      "moduleVersion": "5.2.7.RELEASE",
+      "moduleUrls": [
+        "https://github.com/spring-projects/spring-framework"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework:spring-core",
+      "moduleVersion": "5.2.7.RELEASE",
+      "moduleUrls": [
+        "https://github.com/spring-projects/spring-framework"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework:spring-expression",
+      "moduleVersion": "5.2.7.RELEASE",
+      "moduleUrls": [
+        "https://github.com/spring-projects/spring-framework"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework:spring-jcl",
+      "moduleVersion": "5.2.7.RELEASE",
+      "moduleUrls": [
+        "https://github.com/spring-projects/spring-framework"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework:spring-web",
+      "moduleVersion": "5.2.7.RELEASE",
+      "moduleUrls": [
+        "https://github.com/spring-projects/spring-framework"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.springframework:spring-webmvc",
+      "moduleVersion": "5.2.7.RELEASE",
+      "moduleUrls": [
+        "https://github.com/spring-projects/spring-framework"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
+        }
+      ]
+    },
+    {
+      "moduleName": "org.yaml:snakeyaml",
+      "moduleVersion": "1.25",
+      "moduleUrls": [
+        "http://www.snakeyaml.org"
+      ],
+      "moduleLicenses": [
+        {
+          "moduleLicense": "Apache License, Version 2.0",
+          "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
+        }
+      ]
+    }
+  ]
+}