From 29ad70bd7fb4ad7f9c0df4a754ee799c027a0412 Mon Sep 17 00:00:00 2001
From: Oliver Hecker <8004361+ohecker@users.noreply.github.com>
Date: Thu, 2 Jun 2022 22:47:30 +0200
Subject: [PATCH 001/139] Experimental scancode integration (#120)
---
core/pom.xml | 9 +
.../tools/solicitor/InventoryProcessor.java | 47 +++
.../devonfw/tools/solicitor/Solicitor.java | 18 +-
.../solicitor/common/LicenseTextHelper.java | 32 ++
.../tools/solicitor/common/LogMessages.java | 12 +-
.../common/ResourceToFileCopier.java | 4 +-
.../AbstractSingleKindPackageURLHandler.java | 6 +-
.../tools/solicitor/config/RuleConfig.java | 45 +++
.../model/ModelImporterExporter.java | 9 +
.../model/impl/ModelFactoryImpl.java | 5 +-
.../inventory/ApplicationComponentImpl.java | 80 ++++-
.../impl/inventory/NormalizedLicenseImpl.java | 31 +-
.../model/impl/inventory/RawLicenseImpl.java | 20 +-
.../model/inventory/ApplicationComponent.java | 35 +++
.../model/inventory/NormalizedLicense.java | 7 +
.../solicitor/model/inventory/RawLicense.java | 14 +
.../solicitor/reader/AbstractReader.java | 3 +-
.../solicitor/reader/yarn/YarnReader.java | 6 +-
.../solicitor/ruleengine/RuleEngine.java | 11 +-
.../ruleengine/drools/DroolsRuleEngine.java | 14 +-
.../scancode/ComponentScancodeInfos.java | 227 +++++++++++++++
.../solicitor/scancode/ScancodeAdapter.java | 21 ++
.../solicitor/scancode/ScancodeException.java | 36 +++
.../scancode/ScancodeFileAdapter.java | 273 ++++++++++++++++++
.../scancode/ScancodeInventoryProcessor.java | 193 +++++++++++++
.../solicitor/writer/velocity/EscapeTool.java | 30 ++
.../writer/velocity/VelocityWriter.java | 1 -
.../src/main/resources/application.properties | 14 +
.../tools/solicitor/config/solicitor_base.cfg | 28 +-
...mple.xls => LicenseAssignmentV2Sample.xls} | Bin 60928 -> 61440 bytes
.../rule_templates/LicenseAssignmentV2.drt | 118 ++++++++
...ncomponents_with_noncommerciallicenses.sql | 26 ++
...th_noncommerciallicenses_with_licenses.sql | 31 ++
.../tools/solicitor/sql/noticefiles.sql | 20 ++
.../sql/ossapplicationcomponents.sql | 4 +-
.../sql/ossden_normalizedlicenses.sql | 29 ++
.../tools/solicitor/sql/scancode_sources.sql | 3 +-
...uelicenses_with_application_components.sql | 21 ++
.../tools/solicitor/templates/Attributions.vm | 163 +++++++++++
.../solicitor/templates/ScancodeScanScript.vm | 17 +-
.../solicitor/templates/ScancodeScript.vm | 6 +-
...ect.xls => LicenseAssignmentV2Project.xls} | Bin 32768 -> 32768 bytes
.../impl/MavenPackageURLHandlerTests.java | 10 +
.../test/resources/scancode/curations.json | 2 +
documentation/files/application.properties | 14 +
documentation/files/solicitor_base.cfg | 52 +++-
documentation/master-solicitor.asciidoc | 179 +++++++++++-
47 files changed, 1842 insertions(+), 84 deletions(-)
create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/InventoryProcessor.java
create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java
create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java
create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java
create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java
create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java
create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/EscapeTool.java
rename core/src/main/resources/com/devonfw/tools/solicitor/rules/{LicenseAssignmentSample.xls => LicenseAssignmentV2Sample.xls} (81%)
create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt
create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql
create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql
create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql
create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/ossden_normalizedlicenses.sql
create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql
create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm
rename core/src/main/resources/starters/rules/{LicenseAssignmentProject.xls => LicenseAssignmentV2Project.xls} (79%)
create mode 100644 core/src/test/resources/scancode/curations.json
diff --git a/core/pom.xml b/core/pom.xml
index 781f8384..5376ce74 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -53,6 +53,10 @@
+This section lists all license texts that might be applicable for the third party open source components contained in this product. Each section lists:
+ruleGroup
.
*
@@ -88,6 +94,26 @@ public boolean isOptional() {
return this.optional;
}
+ /**
+ * Gets deprecationWarnOnly.
+ *
+ * @return deprecationWarnOnly
+ */
+ public boolean isDeprecationWarnOnly() {
+
+ return this.deprecationWarnOnly;
+ }
+
+ /**
+ * Gets deprecationDetails.
+ *
+ * @return deprecationDetails
+ */
+ public String getDeprecationDetails() {
+
+ return this.deprecationDetails;
+ }
+
/**
* This method sets the field ruleGroup
.
*
@@ -147,4 +173,23 @@ public void setOptional(boolean optional) {
this.optional = optional;
}
+
+ /**
+ * Set deprecationWarnOnly.
+ *
+ * @param deprecationWarnOnly new value of deprecationWarnOnly.
+ */
+ public void setDeprecationWarnOnly(boolean deprecationWarnOnly) {
+
+ this.deprecationWarnOnly = deprecationWarnOnly;
+ }
+
+ /**
+ * @param deprecationDetails new value of {@link #getDeprecationDetails}.
+ */
+ public void setDeprecationDetails(String deprecationDetails) {
+
+ this.deprecationDetails = deprecationDetails;
+ }
+
}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java
index 07605c96..5da088c5 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java
@@ -117,6 +117,10 @@ private void readApplicationComponents(Application application, JsonNode applica
if (readModelVersion >= LOWEST_VERSION_WITH_PACKAGE_URL) {
packageUrl = applicationComponentNode.get("packageUrl").asText(null);
}
+ JsonNode copyrightNode = applicationComponentNode.get("copyrights");
+ String copyrights = copyrightNode != null ? copyrightNode.asText(null) : null;
+ JsonNode noticeFileUrlNode = applicationComponentNode.get("noticeFileUrl");
+ String noticeFileUrl = noticeFileUrlNode != null ? noticeFileUrlNode.asText(null) : null;
JsonNode normalizedLicensesNode = applicationComponentNode.get("normalizedLicenses");
JsonNode rawLicensesNode = applicationComponentNode.get("rawLicenses");
@@ -130,6 +134,8 @@ private void readApplicationComponents(Application application, JsonNode applica
applicationComponent.setVersion(version);
applicationComponent.setRepoType(repoType);
applicationComponent.setPackageUrl(packageUrl);
+ applicationComponent.setCopyrights(copyrights);
+ applicationComponent.setNoticeFileUrl(noticeFileUrl);
readNormalizedLicenses(applicationComponent, normalizedLicensesNode, readModelVersion);
readRawLicenses(applicationComponent, rawLicensesNode, readModelVersion);
@@ -267,6 +273,8 @@ private void readRawLicenses(ApplicationComponent applicationComponent, JsonNode
String declaredLicense = rawLicenseNode.get("declaredLicense").asText(null);
String licenseUrl = rawLicenseNode.get("licenseUrl").asText(null);
String trace = rawLicenseNode.get("trace").asText(null);
+ JsonNode originNode = rawLicenseNode.get("origin");
+ String origin = originNode != null ? originNode.asText(null) : null;
boolean specialHandling = rawLicenseNode.get("specialHandling").asBoolean();
RawLicense rawLicense = this.modelFactory.newRawLicense();
@@ -274,6 +282,7 @@ private void readRawLicenses(ApplicationComponent applicationComponent, JsonNode
rawLicense.setDeclaredLicense(declaredLicense);
rawLicense.setLicenseUrl(licenseUrl);
rawLicense.setTrace(trace);
+ rawLicense.setOrigin(origin);
rawLicense.setSpecialHandling(specialHandling);
}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java
index 8328cb44..5210fd51 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java
@@ -90,7 +90,10 @@ public Application newApplication(String name, String releaseId, String releaseD
@Override
public ApplicationComponent newApplicationComponent() {
- return new ApplicationComponentImpl();
+ ApplicationComponentImpl result = new ApplicationComponentImpl();
+ result.setLicenseContentProvider(this.licenseContentProvider);
+ return result;
+
}
/** {@inheritDoc} */
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java
index 8212feb3..37251053 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java
@@ -8,7 +8,10 @@
import java.util.Collections;
import java.util.List;
+import com.devonfw.tools.solicitor.common.LicenseTextHelper;
import com.devonfw.tools.solicitor.common.SolicitorRuntimeException;
+import com.devonfw.tools.solicitor.common.content.ContentProvider;
+import com.devonfw.tools.solicitor.common.content.web.WebContent;
import com.devonfw.tools.solicitor.model.impl.AbstractModelObject;
import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent;
import com.devonfw.tools.solicitor.model.inventory.NormalizedLicense;
@@ -32,6 +35,8 @@ public class ApplicationComponentImpl extends AbstractModelObject implements App
private String ossHomepage;
+ private String noticeFileUrl;
+
private String groupId;
private String artifactId;
@@ -42,10 +47,14 @@ public class ApplicationComponentImpl extends AbstractModelObject implements App
private String packageUrl;
+ private String copyrights;
+
private ListlicenseContentProvider
.
+ *
+ * @return the field licenseContentProvider
+ */
+ @JsonIgnore
+ public ContentProviderlicenseContentProvider
.
+ *
+ * @param licenseContentProvider the new value of the field licenseContentProvider
+ */
+ public void setLicenseContentProvider(ContentProviderapplication
.
*
@@ -59,6 +64,20 @@ public interface ApplicationComponent {
*/
String getOssHomepage();
+ /**
+ * This method gets the field noticeFileUrl
.
+ *
+ * @return the field noticeFileUrl
+ */
+ String getNoticeFileUrl();
+
+ /**
+ * This method gets the field noticeFileContent
.
+ *
+ * @return the field noticeFileContent
+ */
+ String getNoticeFileContent();
+
/**
* This method gets an unmodifiable copy of the field rawLicenses
.
*
@@ -104,6 +123,8 @@ public interface ApplicationComponent {
*/
boolean isOssModified();
+ String getCopyrights();
+
/**
* Sets the {@link Application}.
*
@@ -132,6 +153,13 @@ public interface ApplicationComponent {
*/
void setOssHomepage(String ossHomepage);
+ /**
+ * This method sets the field noticeFileUrl
.
+ *
+ * @param noticeFileUrl the new value of the field noticeFileUrl
+ */
+ void setNoticeFileUrl(String noticeFileUrl);
+
/**
* This method sets the field ossModified
.
*
@@ -160,6 +188,13 @@ public interface ApplicationComponent {
*/
void setRepoType(String repoType);
+ /**
+ * This method sets the field copyrights
.
+ *
+ * @param copyrights the new value of the field copyrights
+ */
+ void setCopyrights(String copyrights);
+
/**
* This method sets the field packageUrl
.
*
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java
index 165ef7fe..73d53ec9 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java
@@ -153,6 +153,13 @@ public interface NormalizedLicense {
*/
String getNormalizedLicenseUrl();
+ /**
+ * Gets the content referenced by {@link #getNormalizedLicenseUrl()}
+ *
+ * @return the referenced content.
+ */
+ String getNormalizedLicenseContent();
+
/**
* This method gets the field reviewedForRelease
.
*
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java
index 78d5aaca..41c5322d 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java
@@ -33,6 +33,13 @@ public interface RawLicense {
*/
String getTrace();
+ /**
+ * This method gets the field origin
.
+ *
+ * @return the field origin
+ */
+ String getOrigin();
+
/**
* This method gets the field specialHandling
.
*
@@ -75,6 +82,13 @@ public interface RawLicense {
*/
void setTrace(String trace);
+ /**
+ * This method sets the field origin
.
+ *
+ * @param origin the new value of the field origin
+ */
+ void setOrigin(String origin);
+
/**
* Complete the data of this object by setting members which are derived from other members.
*/
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/AbstractReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/AbstractReader.java
index f57195fd..7a7045fe 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/reader/AbstractReader.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/AbstractReader.java
@@ -59,10 +59,11 @@ public void doLogging(String sourceUrl, Application application, int readCompone
*/
public void addRawLicense(ApplicationComponent appComponent, String name, String url, String path) {
- RawLicense mlic = modelFactory.newRawLicense();
+ RawLicense mlic = this.modelFactory.newRawLicense();
mlic.setApplicationComponent(appComponent);
mlic.setDeclaredLicense(name);
mlic.setLicenseUrl(url);
+ mlic.setOrigin(this.getClass().getSimpleName().toLowerCase());
String trace;
if (name == null && url == null) {
trace = "+ Component info (without license) read in '" + getSupportedTypes() + "' format from '" + path + "'";
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java
index 3eac7b7b..ca6b5461 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java
@@ -76,7 +76,11 @@ public void readInventory(String type, String sourceUrl, Application application
// check whether VendorUrl is included in input file or not
if (attributes.size() == 6) {
- homePage = attributes.get(4);
+ if (attributes.get(4) != null && !attributes.get(4).isBlank()) {
+ homePage = attributes.get(4);
+ } else {
+ homePage = repo;
+ }
} else if (attributes.size() == 5) {
homePage = repo;
}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/RuleEngine.java b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/RuleEngine.java
index dd97908d..56d1bbdf 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/RuleEngine.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/RuleEngine.java
@@ -4,18 +4,11 @@
package com.devonfw.tools.solicitor.ruleengine;
-import com.devonfw.tools.solicitor.model.ModelRoot;
+import com.devonfw.tools.solicitor.InventoryProcessor;
/**
* Interface of a rule engine which works on the data of the Solicitor data model.
*/
-public interface RuleEngine {
-
- /**
- * Executes all rules.
- *
- * @param modelRoot the {@link ModelRoot} which is the root objects of the data model to be processed.
- */
- public void executeRules(ModelRoot modelRoot);
+public interface RuleEngine extends InventoryProcessor {
}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java
index a34d8633..41778355 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java
@@ -23,9 +23,12 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
+import com.devonfw.tools.solicitor.InventoryProcessor;
import com.devonfw.tools.solicitor.SolicitorSetup;
+import com.devonfw.tools.solicitor.common.DeprecationChecker;
import com.devonfw.tools.solicitor.common.InputStreamFactory;
import com.devonfw.tools.solicitor.common.LogMessages;
import com.devonfw.tools.solicitor.config.RuleConfig;
@@ -43,6 +46,7 @@
*
*/
@Component
+@Order(InventoryProcessor.RULE_ENGINE)
public class DroolsRuleEngine implements RuleEngine {
private static final Logger LOG = LoggerFactory.getLogger(DroolsRuleEngine.class);
@@ -59,13 +63,16 @@ public class DroolsRuleEngine implements RuleEngine {
@Autowired
private SolicitorSetup setup;
+ @Autowired
+ private DeprecationChecker deprecationChecker;
+
/**
* {@inheritDoc}
*
* Each set of rules given by a RuleConfig (e.g. a single decision table) will be executed in a separate Kie session.
*/
@Override
- public void executeRules(ModelRoot modelRoot) {
+ public void processInventory(ModelRoot modelRoot) {
int rulesFired = 0;
for (RuleConfig rc : this.setup.getRuleSetups()) {
@@ -93,6 +100,11 @@ private int executeRuleGroup(ModelRoot modelRoot, RuleConfig rc) {
return 0;
}
+ // check if this rule group was marked as deprecated
+ if (rc.getDeprecationDetails() != null && !rc.getDeprecationDetails().isBlank()) {
+ this.deprecationChecker.check(rc.isDeprecationWarnOnly(), rc.getDeprecationDetails());
+ }
+
KieSession ksession = prepareSession(rc);
insertFacts(ksession, modelRoot);
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java
new file mode 100644
index 00000000..7c6a0f89
--- /dev/null
+++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java
@@ -0,0 +1,227 @@
+package com.devonfw.tools.solicitor.scancode;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * Data structure for holding the scan information from scancode for a single package.
+ */
+public class ComponentScancodeInfos {
+
+ /**
+ * Holds the info about a single found license.
+ */
+ public class LicenseInfo {
+
+ /**
+ * Id for of the license.
+ */
+ public String id;
+
+ /**
+ * SPDX-ID of the license.
+ */
+ public String spdxid;
+
+ /**
+ * The score of the license.
+ */
+ public double licenseScore;
+
+ /**
+ * Path to the license file.
+ */
+ public String licenseFilePath;
+
+ /**
+ * The score of the license file.
+ */
+ public double licenseFileScore;
+
+ /**
+ * The constructor.
+ *
+ * @param id the id
+ * @param spdxid the spdx id
+ * @param defaultUrl the default URL of the license text
+ * @param licenseScore the score for the license
+ * @param licenseFilePath the path to the license file
+ * @param licenseFileScore the score of the license file
+ */
+ public LicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseFilePath,
+ double licenseFileScore) {
+
+ super();
+ this.id = id;
+ this.spdxid = spdxid;
+ this.licenseScore = licenseScore;
+ if (licenseFileScore >= ComponentScancodeInfos.this.minLicensefilePercentage) {
+ this.licenseFilePath = licenseFilePath;
+ this.licenseFileScore = licenseFileScore;
+ } else {
+ this.licenseFilePath = defaultUrl;
+ this.licenseFileScore = 0.0;
+ }
+ }
+ }
+
+ private static final double MIN_NOTICEFILE_PERCENTAGE = 0.0;
+
+ private SortedSetnull
is
+ * returned if no data is available,
+ * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available
+ * no exception will be thrown. Instead null
will be return in such a case.
+ */
+ ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throws ScancodeException;
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java
new file mode 100644
index 00000000..9b9eb761
--- /dev/null
+++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java
@@ -0,0 +1,36 @@
+package com.devonfw.tools.solicitor.scancode;
+
+/**
+ * Exception indicating that the Scancode information could not be read.
+ */
+public class ScancodeException extends Exception {
+
+ /**
+ * A constructor.
+ */
+ public ScancodeException() {
+
+ }
+
+ /**
+ * A constructor.
+ *
+ * @param message the message of the exception
+ */
+ public ScancodeException(String message) {
+
+ super(message);
+ }
+
+ /**
+ * A constructor.
+ *
+ * @param message the message of the exception
+ * @param cause the underlying {@link Throwable}.
+ */
+ public ScancodeException(String message, Throwable cause) {
+
+ super(message, cause);
+ }
+
+}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java
new file mode 100644
index 00000000..791948b5
--- /dev/null
+++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java
@@ -0,0 +1,273 @@
+package com.devonfw.tools.solicitor.scancode;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import com.devonfw.tools.solicitor.common.LogMessages;
+import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+
+/**
+ * Adapter for reading Scancode information for a package from a file, applying any given curations and returning the
+ * information as a {@link ComponentScancodeInfos} object.
+ */
+@Component
+public class ScancodeFileAdapter implements ScancodeAdapter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ScancodeFileAdapter.class);
+
+ private static final ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
+
+ private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
+
+ private String repoBasePath;
+
+ private String curationsFileName;
+
+ private double minLicenseScore;
+
+ private double minLicensefilePercentage;
+
+ private boolean curationsExistenceLogged;
+
+ @Autowired
+ private AllKindsPackageURLHandler packageURLHandler;
+
+ /**
+ * The constructor.
+ */
+
+ public ScancodeFileAdapter() {
+
+ }
+
+ /**
+ * Sets repoBasePath.
+ *
+ * @param repoBasePath new value of repoBasePath.
+ */
+ @Value("${solicitor.scancode.repo-base-path}")
+ public void setRepoBasePath(String repoBasePath) {
+
+ this.repoBasePath = repoBasePath;
+ }
+
+ /**
+ * Sets curationsFileName.
+ *
+ * @param curationsFileName new value of curationsFileName.
+ */
+ @Value("${solicitor.scancode.curations-filename}")
+ public void setCurationsFileName(String curationsFileName) {
+
+ this.curationsFileName = curationsFileName;
+ }
+
+ /**
+ * Sets minLicenseScore.
+ *
+ * @param minLicenseScore new value of minLicenseScore.
+ */
+ @Value("${solicitor.scancode.min-license-score}")
+ public void setMinLicenseScore(double minLicenseScore) {
+
+ this.minLicenseScore = minLicenseScore;
+ }
+
+ /**
+ * Sets minLicensefilePercentage.
+ *
+ * @param minLicensefilePercentage new value of minLicensefilePercentage.
+ */
+ @Value("${solicitor.scancode.min-licensefile-percentage}")
+ public void setMinLicensefilePercentage(double minLicensefilePercentage) {
+
+ this.minLicensefilePercentage = minLicensefilePercentage;
+ }
+
+ /**
+ * Retrieves the Scancode information and curations for a package identified by the given package URL. Returns the
+ * data as a {@link ComponentScancodeInfos} object.
+ *
+ * @param packageUrl The identifier of the package for which information is requested
+ * @return the data derived from the scancode results after applying any defined curations. null
is
+ * returned if no data is available,
+ * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available
+ * no exception will be thrown. Instead null
will be return in such a case.
+ */
+ @Override
+ public ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throws ScancodeException {
+
+ ComponentScancodeInfos componentScancodeInfos = determineScancodeInformation(packageUrl);
+ if (componentScancodeInfos == null) {
+ return null;
+ }
+ applyCurations(packageUrl, componentScancodeInfos);
+
+ return componentScancodeInfos;
+
+ }
+
+ /**
+ * Read scancode information for the given package from local file storage.
+ *
+ * @param packageUrl The package url of the package
+ * @return the read scancode information, null
if no information was found
+ * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available
+ * no exception will be thrown. Instead null
will be return in such a case.
+ */
+ private ComponentScancodeInfos determineScancodeInformation(String packageUrl) throws ScancodeException {
+
+ ComponentScancodeInfos componentScancodeInfos = new ComponentScancodeInfos(this.minLicenseScore,
+ this.minLicensefilePercentage);
+ String packagePathPart = this.packageURLHandler.pathFor(packageUrl);
+ String path = this.repoBasePath + "/" + packagePathPart + "/scancode.json";
+
+ File scanCodeFile = new File(path);
+ if (!scanCodeFile.exists()) {
+ LOG.debug("No Scancode info available for PackageURL '{}'", packageUrl);
+ return null;
+ }
+ LOG.debug("Found Scancode info for PackageURL '{}'", packageUrl);
+ try (InputStream is = new FileInputStream(scanCodeFile)) {
+
+ JsonNode scancodeJson = mapper.readTree(is);
+
+ for (JsonNode file : scancodeJson.get("files")) {
+ if ("directory".equals(file.get("type").asText())) {
+ continue;
+ }
+ if (file.get("path").asText().contains("/NOTICE")) {
+ componentScancodeInfos.addNoticeFilePath("file:" + this.repoBasePath + "/"
+ + this.packageURLHandler.pathFor(packageUrl) + "/" + file.get("path").asText(), 100.0);
+ }
+ for (JsonNode cr : file.get("copyrights")) {
+ componentScancodeInfos.addCopyright(cr.get("value").asText());
+ }
+
+ for (JsonNode li : file.get("licenses")) {
+ String licenseid = li.get("key").asText();
+ String licenseName = li.get("spdx_license_key").asText();
+ String licenseDefaultUrl = li.get("scancode_text_url").asText();
+ licenseDefaultUrl = normalizeLicenseUrl(packageUrl, licenseDefaultUrl);
+ double score = li.get("score").asDouble();
+ String licenseFilePath = file.get("path").asText();
+ licenseFilePath = normalizeLicenseUrl(packageUrl, licenseFilePath);
+
+ componentScancodeInfos.addLicense(licenseid, licenseName, licenseDefaultUrl, score, licenseFilePath,
+ file.get("percentage_of_license_text").asDouble());
+ }
+ }
+ LOG.debug("Scancode info for package {}: {} license, {} copyrights, {} NOTICE files", packageUrl,
+ componentScancodeInfos.getLicenses().size(), componentScancodeInfos.getCopyrights().size(),
+ componentScancodeInfos.getNoticeFilePath() != null ? 1 : 0);
+
+ } catch (IOException e) {
+ throw new ScancodeException("Could not read Scancode JSON", e);
+ }
+ return componentScancodeInfos;
+ }
+
+ /**
+ * Checks for the existence of curations for the given package in the local file system and applies them to the
+ * component scancode infos.
+ *
+ * @param packageUrl the identifier of the package
+ * @param componentScancodeInfos the componentScancodeInfos to curate
+ * @throws ScancodeException if the curations could not be read
+ */
+ private void applyCurations(String packageUrl, ComponentScancodeInfos componentScancodeInfos)
+ throws ScancodeException {
+
+ String packagePathPart = this.packageURLHandler.pathFor(packageUrl);
+
+ File curationsFile = new File(this.curationsFileName);
+ if (!curationsFile.exists()) {
+ if (!this.curationsExistenceLogged) {
+ // log only once
+ this.curationsExistenceLogged = true;
+ LOG.info(LogMessages.CURATIONS_NOT_EXISTING.msg(), this.curationsFileName);
+ }
+ } else {
+ if (!this.curationsExistenceLogged) {
+ // log only once
+ this.curationsExistenceLogged = true;
+ LOG.info(LogMessages.CURATIONS_PROCESSING.msg(), this.curationsFileName);
+ }
+ try (InputStream isc = new FileInputStream(this.curationsFileName)) {
+
+ JsonNode curationsObj = yamlMapper.readTree(isc);
+
+ for (JsonNode curations : curationsObj.get("artifacts")) {
+ String component = curations.get("name").asText();
+ if (component.equals(packagePathPart)) {
+ ComponentScancodeInfos oneComponent = componentScancodeInfos;
+ if (curations.get("copyrights") != null) {
+ oneComponent.clearCopyrights();
+ int authorCount = curations.get("copyrights").size();
+ for (int i = 0; i < authorCount; i++) {
+ oneComponent.addCopyright(curations.get("copyrights").get(i).asText());
+ }
+ }
+ if (curations.get("url") != null) {
+ String url = curations.get("url").asText();
+ oneComponent.setUrl(url);
+ }
+ if (curations.get("licenses") != null) {
+ oneComponent.clearLicenses();
+ for (JsonNode licenseNode : curations.get("licenses")) {
+ String license = licenseNode.get("license").asText();
+ String url = licenseNode.get("url").asText();
+ oneComponent.addLicense(license, license, "", 110, url, 110);
+ }
+ }
+ }
+ }
+
+ } catch (IOException e) {
+ throw new ScancodeException("Could not read Curations JSON", e);
+ }
+
+ }
+
+ }
+
+ /**
+ * Adjustment of license paths/urls so that they might retrieved
+ *
+ * @param packageUrl package url of the package
+ * @param licenseFilePath the original path or URL
+ * @return the adjustes patjh or url as a url
+ */
+ private String normalizeLicenseUrl(String packageUrl, String licenseFilePath) {
+
+ String adjustedLicenseFilePath;
+ if (licenseFilePath != null) {
+ if (licenseFilePath.startsWith("http")) {
+ // TODO
+ adjustedLicenseFilePath = licenseFilePath.replace("github.com", "raw.github.com").replace("/tree", "");
+ } else {
+ adjustedLicenseFilePath = "file:" + this.repoBasePath + "/" + this.packageURLHandler.pathFor(packageUrl) + "/"
+ + licenseFilePath;
+ LOG.debug("LOCAL LICENSE: " + licenseFilePath);
+ }
+ } else {
+ adjustedLicenseFilePath = null;
+ // licenseFilePath = null;// "??????";// defaultGithubLicenseURL(repo);
+ LOG.debug("NONLOCAL LICENSE: {} (was: {})" + adjustedLicenseFilePath, licenseFilePath);
+ }
+ return adjustedLicenseFilePath;
+ }
+
+}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java
new file mode 100644
index 00000000..9f2edece
--- /dev/null
+++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java
@@ -0,0 +1,193 @@
+package com.devonfw.tools.solicitor.scancode;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import com.devonfw.tools.solicitor.InventoryProcessor;
+import com.devonfw.tools.solicitor.common.LogMessages;
+import com.devonfw.tools.solicitor.common.SolicitorRuntimeException;
+import com.devonfw.tools.solicitor.model.ModelFactory;
+import com.devonfw.tools.solicitor.model.ModelRoot;
+import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent;
+import com.devonfw.tools.solicitor.model.inventory.RawLicense;
+import com.devonfw.tools.solicitor.model.masterdata.Application;
+import com.devonfw.tools.solicitor.scancode.ComponentScancodeInfos.LicenseInfo;
+
+/**
+ * An {@link InventoryProcessor} which looks up license information for the found application components / packages in
+ * the scancode file store. If license information is found then the license, copyright and notice file information of
+ * the model will be replaced by the data obtained from scancode.
+ *
+ */
+@Component
+@Order(InventoryProcessor.BEFORE_RULE_ENGINE)
+public class ScancodeInventoryProcessor implements InventoryProcessor {
+
+ private static class Statistics {
+ public int componentsTotal;
+
+ public int componentsWithScancodeInfos;
+
+ public void add(Statistics statistics) {
+
+ this.componentsTotal += statistics.componentsTotal;
+ this.componentsWithScancodeInfos += statistics.componentsWithScancodeInfos;
+ }
+ }
+
+ /**
+ * Origin data for raw license objects created by this Class.
+ */
+ private static final String ORIGIN_SCANCODE = "scancode";
+
+ private static final Logger LOG = LoggerFactory.getLogger(ScancodeInventoryProcessor.class);
+
+ private boolean featureFlag = false;
+
+ private ScancodeAdapter scancodeAdapter;
+
+ private ModelFactory modelFactory;
+
+ /**
+ * Sets the feature flag for activating/deactivating this feature.
+ *
+ * @param featureFlag the flag
+ */
+ @Value("${solicitor.feature-flag.scancode}")
+ public void setFeatureFlag(boolean featureFlag) {
+
+ this.featureFlag = featureFlag;
+ }
+
+ /**
+ * The constructor.
+ */
+ public ScancodeInventoryProcessor() {
+
+ }
+
+ @Override
+ public void processInventory(ModelRoot modelRoot) {
+
+ if (!this.featureFlag) {
+ LOG.info(LogMessages.SCANCODE_FEATURE_DEACTIVATED.msg());
+ } else {
+ LOG.warn(LogMessages.SCANCODE_PROCESSOR_STARTING.msg());
+
+ Statistics overall = new Statistics();
+ for (Application application : modelRoot.getEngagement().getApplications()) {
+ for (ApplicationComponent ac : application.getApplicationComponents()) {
+ Statistics single = processApplicationComponent(ac);
+ overall.add(single);
+ }
+ }
+ LOG.info(LogMessages.SCANCODE_INFO_READ.msg(), overall.componentsWithScancodeInfos, overall.componentsTotal);
+ }
+ }
+
+ /**
+ * Process a single {@link ApplicationComponent}.
+ *
+ * @param ac application component
+ * @return processing statistics
+ */
+ private Statistics processApplicationComponent(ApplicationComponent ac) {
+
+ Statistics statistics = new Statistics();
+ statistics.componentsTotal = 1;
+
+ if (ac.getPackageUrl() != null) {
+ ComponentScancodeInfos componentScancodeInfos;
+ try {
+ componentScancodeInfos = this.scancodeAdapter.getComponentScancodeInfos(ac.getPackageUrl());
+ } catch (ScancodeException e) {
+ throw new SolicitorRuntimeException("Exception when reading scancode file", e);
+ }
+ if (componentScancodeInfos != null) {
+ statistics.componentsWithScancodeInfos = 1;
+ ac.removeAllRawLicenses();
+
+ if (componentScancodeInfos.getNoticeFilePath() != null) {
+ ac.setNoticeFileUrl(componentScancodeInfos.getNoticeFilePath());
+ }
+
+ for (LicenseInfo li : componentScancodeInfos.getLicenses().values()) {
+ addRawLicense(ac, li.spdxid, li.licenseFilePath, ORIGIN_SCANCODE);
+ }
+ String copyrights = String.join("\n", componentScancodeInfos.getCopyrights());
+ ac.setCopyrights(copyrights);
+ // check whether VendorUrl is included in input file or not
+ if (componentScancodeInfos.getUrl() != null) {
+ ac.setOssHomepage(componentScancodeInfos.getUrl());
+ }
+ } else {
+ // no scancode info found for ac
+ }
+ } else {
+ // can this happen?
+ }
+ return statistics;
+ }
+
+ /**
+ * Performs logging.
+ *
+ * @param sourceUrl the URL from where the inventory data was read
+ * @param application the application
+ * @param readComponents number of read ApplicationComponents
+ * @param readLicenses number of read RawLicenses
+ */
+ public void doLogging(String sourceUrl, Application application, int readComponents, int readLicenses) {
+
+ LOG.info(LogMessages.READING_INVENTORY.msg(), readComponents, readLicenses, application.getName(), sourceUrl);
+ }
+
+ /**
+ * Adds a {@link com.devonfw.tools.solicitor.model.inventory.RawLicense} to the given
+ * {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent}.
+ *
+ * @param appComponent a {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent} object.
+ * @param name a {@link java.lang.String} object.
+ * @param url a {@link java.lang.String} object.
+ * @param origin a {@link java.lang.String} object.
+ */
+ public void addRawLicense(ApplicationComponent appComponent, String name, String url, String origin) {
+
+ RawLicense mlic = this.modelFactory.newRawLicense();
+ mlic.setApplicationComponent(appComponent);
+ mlic.setDeclaredLicense(name);
+ mlic.setLicenseUrl(url);
+ mlic.setOrigin(origin);
+ String trace;
+ trace = "+ Component/License info read from scancode information";
+
+ mlic.setTrace(trace);
+ }
+
+ /**
+ * This method sets the field modelFactory
.
+ *
+ * @param modelFactory the new value of the field modelFactory
+ */
+ @Autowired
+ public void setModelFactory(ModelFactory modelFactory) {
+
+ this.modelFactory = modelFactory;
+ }
+
+ /**
+ * This method sets the field scancodeAdapter
+ *
+ * @param scancodeAdapter new value of scancodeAdapter
.
+ */
+ @Autowired
+ public void setScancodeAdapter(ScancodeAdapter scancodeAdapter) {
+
+ this.scancodeAdapter = scancodeAdapter;
+ }
+
+}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/EscapeTool.java b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/EscapeTool.java
new file mode 100644
index 00000000..bd998014
--- /dev/null
+++ b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/EscapeTool.java
@@ -0,0 +1,30 @@
+package com.devonfw.tools.solicitor.writer.velocity;
+
+import org.apache.commons.codec.digest.DigestUtils;
+
+import com.devonfw.tools.solicitor.common.LicenseTextHelper;
+
+/**
+ * TODO ohecker This type ...
+ *
+ */
+public class EscapeTool extends org.apache.velocity.tools.generic.EscapeTool {
+
+ public String newLinesAsBreaks(String input) {
+
+ return input != null ? input.replace("\n", "
") : null;
+ }
+
+ public String wrap100to80(String input) {
+
+ return LicenseTextHelper.wrapIfNecessary(input, 100, 80);
+ }
+
+ public String hash(String input) {
+
+ if (input == null) {
+ return "";
+ }
+ return new DigestUtils("SHA-256").digestAsHex(input);
+ }
+}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java
index 4189d92c..0669e6b3 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java
@@ -14,7 +14,6 @@
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
-import org.apache.velocity.tools.generic.EscapeTool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties
index a399ea39..874da74d 100644
--- a/core/src/main/resources/application.properties
+++ b/core/src/main/resources/application.properties
@@ -41,6 +41,20 @@ solicitor.classpath-guessedlicenseurl-cache-locations=licenseurls
# Deprecated features are deactivated by default. If set to true they might be (temporarily) activated.
solicitor.deprecated-features-allowed=false
+## Feature flags for activation of non-standard/experimental functionality
+# Incorporate scancode infos into model
+solicitor.feature-flag.scancode=false
+
+## Parameters for controlling the processing of scancode information
+# minimum score of detected license findings to be taken into account for Solicitor processing
+solicitor.scancode.min-license-score=80.0
+# minimum "percentage_of_license_text" for a file to be possibly taken as license text
+solicitor.scancode.min-licensefile-percentage=50.0
+# filename of the curationy.yaml file
+solicitor.scancode.curations-filename=output/curations.yaml
+# base path of the file repo where sources and scancode information is stored
+solicitor.scancode.repo-base-path=output/Source
+
# If there is an exception while reading the raw inventory data for an application the processing will be aborted by default.
# By setting this property to true processing will continue in case that the input file is not found. This allows processing
# in multi application projects where some input files are not yet available. A warning (SOLI-045) will be logged in this case and
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg
index 0d00e8d5..549ec9a2 100644
--- a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg
@@ -26,13 +26,22 @@
"ruleSource" : "file:${cfgdir}/rules/LicenseAssignmentProject",
"templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt",
"ruleGroup" : "LicenseAssignmentProject",
+ "description" : "setting license in case that no one was detected or overwriting a wrongly detected license (project overrides; 'old' rule structure)",
+ "deprecationWarnOnly" : true,
+ "deprecationDetails" : "Use of decision table LicenseAssignmentProject is deprecated, use LicenseAssignmentV2Project instead. See release notes of release 1.4.0 for migration hints"
+ },{
+ "type" : "dt",
+ "optional" : true,
+ "ruleSource" : "file:${cfgdir}/rules/LicenseAssignmentV2Project",
+ "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt",
+ "ruleGroup" : "LicenseAssignmentV2Project",
"description" : "setting license in case that no one was detected or overwriting a wrongly detected license (project overrides)"
},{
"type" : "dt",
"optional" : false,
- "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseAssignmentSample",
- "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt",
- "ruleGroup" : "LicenseAssignmentDefaults",
+ "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample",
+ "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt",
+ "ruleGroup" : "LicenseAssignmentV2Defaults",
"description" : "setting license in case that no one was detected or overwriting a wrongly detected license"
},{
"type" : "dt",
@@ -150,6 +159,19 @@
"OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents_guessedlicenses.sql",
"UNIQUELICENSES" : "classpath:com/devonfw/tools/solicitor/sql/uniqueguessedlicenses.sql"
}
+ },{
+ "type" : "velo",
+ "templateSource" : "classpath:com/devonfw/tools/solicitor/templates/Attributions.vm",
+ "target" : "${cfgdir}/output/Attributions_${project}.html",
+ "description" : "A document containing the license information and attributions required by the OSS licenses",
+ "dataTables" : {
+ "ENGAGEMENT" : "classpath:com/devonfw/tools/solicitor/sql/allden_engagements.sql",
+ "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql",
+ "UNIQUELICENSES" : "classpath:com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql",
+ "NOTICEFILES" : "classpath:com/devonfw/tools/solicitor/sql/noticefiles.sql",
+ "NONCOMMERCIALCOMPONENTS" : "classpath:com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql",
+ "NONCOMMERCIALCOMPONENTS_LICENSES" : "classpath:com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql"
+ }
},{
"type" : "velo",
"templateSource" : "classpath:com/devonfw/tools/solicitor/templates/Solicitor_Diff_Template_Sample.vm",
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentSample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls
similarity index 81%
rename from core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentSample.xls
rename to core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls
index bf73870a55b13eb4cfda501a21e30b20777b8c8d..dfdc7ad23235e7226fd0885bc3267ab877b038a5 100644
GIT binary patch
delta 6439
zcmZvgYitzP701uauD#FMUBA{|zu)gy8Xf@yF~w^G7A&YhsH!HVw2eqD;?RT-5y5Ur
z2;q@fp5nIHO(;T&8qvyP+bAs*Disy_qE0E$rZloD6sb)`sl*3T(b9A7+_^JnJsR1X
zbMC#r^S|dlXUA{(#^-&rem(Ro0Q~Ra!-u0;Lw{R~?v^IG%%A&;wY}#1zK%)%NPh2?
z8$Z4Gyt&7JP(OS<`yc-=e8C;>pyyATZf@?`-H_x^~&T8>tij%fiJlp&?8Mawg67
z(TFHWnj`A%b@A43l!a5GvPPB9N2^4|V)IVakZ%dYdP_z%SOVvD$?Z4lYA)w#t6>l9
zf*mjdk3cot!opQf>~X^uw8C+cpbrcZ^pPkI#=1nLGIL`rgm3*u(%c=x^6ZPT<(=B!
z%^xp69=KNY1a|0Tc5~mmMP>N_nLWFn-nBalurae``PX*t*uC@FFMT;1$lQq)uG|X{
zcmv?TdjO%I0X%mx8+h_q{YZQW^8xS@L?MXJyB&@lf)F(JJ9ZeturBG?5s1KKkz+@Z
z(U@{<0}O!GjvX`aj;F>x!DvA}f&T&G&_@FT{aRf3$cV#3a>2Nk7Cus!>+^Z|{2so%
zg}yPsJS_kQm~WE^9ArLAOvu9*_V7hKd^ju^cZ~NAGqgZV%)=M=@KFvw$;noMhp*7>
z8N!suc~@l8`&myVYG$NhD<8RiPoN5Rx7ZaYzb62IJ$n#y-)K
zP-4As^17zoGk2cyLTeOgjRLLl$cjVF|3Pb9=v#AZ@EPt#ZS-8M;IYzku|ff^@TjCV
z+DB*yXnx(BT`RL|mF!xNoCws)>@%FbN%UPOv+I=XI;HPAnO*Y+_3dQW%j|k3yI$$L
zUS`j|(KeOFBfq_uG;sD>X?b^!2BrT7hX6OQ0%drv8rLtuQF+b5oV=fb8N7cEzeMC;
zWBQmrF$EKR(E+j%yUst`HTn3OeSEY5=jXQi_}cP(e43^6lbdXx?Px!BefBwycC36K
z?SuI~T1ol7l*b3+djp=v+|#V%q*t|@Y>T1eLB5Y(@cF(0AK%w}e5<6-bDE`hVm@$<
zkB`<%ejKfud>@@d^L=!#RD9z&@AR@)oah_$@zG(;iF2y8(Z@%RLw?+5@|hRk?8jG>
zv)~(*1>dMVGL6b3)95UCvwMDXT^Khe=YZVAada%84WkL7=BfEiDh%6YL^Jxg<91<3
zG$Uyn9Fdk`mYrTXNZU>e9s#y9r3H0m;IN2kL0xWtElPha*x%99Yg69%RwcewiEqXD
z|DGQ0%HWaE*3-s)?Ua_cuQrI7lV`q}8G$!Ndpkytz`V4=XgQT=$DX{AQP>5iF>(Pu
z)!MgJVwpPs!_@;@Vw!`T1;
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt
new file mode 100644
index 00000000..5fd79b30
--- /dev/null
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: Apache-2.0
+
+template header
+ruleId
+clientName
+engagementName
+applicationName
+groupId
+artifactId
+version
+origin
+declaredLicense
+url
+normalizedLicenseType
+normalizedLicense
+normalizedLicenseUrl
+comment
+
+
+package com.devonfw.tools.solicitor.rules;
+
+import com.devonfw.tools.solicitor.model.inventory.NormalizedLicense;
+import com.devonfw.tools.solicitor.model.inventory.RawLicense;
+import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent;
+import com.devonfw.tools.solicitor.model.masterdata.Application;
+import com.devonfw.tools.solicitor.model.masterdata.Engagement;
+import com.devonfw.tools.solicitor.ruleengine.drools.AuditEntryBuilder;
+import com.devonfw.tools.solicitor.ruleengine.drools.ModelHelper;
+
+
+template "License Assignment"
+
+rule "License Assignment @{ruleId} - mark RawLicense as done by specialHandling" salience -10000
+ when
+ // make sure that the main conditions given here are the same as in the below rule
+ // this rule fires at the very end (salience -10000) of the group and
+ // assures that all records which have been processed by the other rule get
+ // marked with "specialHandling=true" so they wont be handled again
+ $e : Engagement(
+ clientName == "@{clientName}",
+ engagementName == "@{engagementName}",
+ $name : engagementName )
+ $a : Application(
+ name == "@{applicationName}",
+ engagement == $e )
+ $ac : ApplicationComponent(
+ ModelHelper.match( groupId, "@{groupId}" ),
+ ModelHelper.match( artifactId, "@{artifactId}" ),
+ ModelHelper.match( version, "@{version}" ),
+ application == $a )
+ $rl : RawLicense(
+ specialHandling == false,
+ ModelHelper.match( origin, "@{origin}" ),
+ ModelHelper.match( declaredLicense, "@{declaredLicense}" ),
+ $licUrl : licenseUrl,
+ ModelHelper.match( licenseUrl, "@{url}" ),
+ applicationComponent == $ac )
+ then
+ $rl.setSpecialHandling(true);
+ update($rl);
+
+end
+
+rule "License Assignment @{ruleId} - add given NormalizedLicense" salience -@{row.rowNumber}
+ when
+ $e : Engagement(
+ clientName == "@{clientName}",
+ engagementName == "@{engagementName}",
+ $name : engagementName )
+ $a : Application(
+ name == "@{applicationName}",
+ engagement == $e )
+ $ac : ApplicationComponent(
+ ModelHelper.match( groupId, "@{groupId}" ),
+ ModelHelper.match( artifactId, "@{artifactId}" ),
+ ModelHelper.match( version, "@{version}" ),
+ application == $a )
+ $rl : RawLicense(
+ specialHandling == false,
+ ModelHelper.match( origin, "@{origin}" ),
+ ModelHelper.match( declaredLicense, "@{declaredLicense}" ),
+ $licUrl : licenseUrl,
+ ModelHelper.match( licenseUrl, "@{url}" ),
+ applicationComponent == $ac )
+ not( NormalizedLicense(applicationComponent == $ac, normalizedLicense == "@{normalizedLicense}") ) // differs from above
+
+ then
+ NormalizedLicense normalizedLicense = ModelHelper.newNormalizedLicense($rl );
+ normalizedLicense.setNormalizedLicenseType("@{normalizedLicenseType}");
+ normalizedLicense.setNormalizedLicense("@{normalizedLicense}");
+ normalizedLicense.setNormalizedLicenseUrl($licUrl);
+ normalizedLicense.setNormalizedLicenseUrl( "@{normalizedLicenseUrl}" );
+ ModelHelper.addCommentToNormalizedLicense(normalizedLicense, "@{comment}" );
+
+ // Create trace entry
+ AuditEntryBuilder aeb = AuditEntryBuilder.instance();
+ aeb.withRuleId("@{ruleId}")
+ .withMatching("clientName", "@{clientName}" )
+ .withMatching("engagementName", "@{engagementName}" )
+ .withMatching("name", "@{applicationName}" )
+ .withMatching("groupId", "@{groupId}" )
+ .withMatching("artifactId", "@{artifactId}" )
+ .withMatching("version", "@{version}" )
+ .withMatching("declaredLicense", "@{declaredLicense}" )
+ .withMatching("licenseUrl", "@{url}" )
+ .withSetting("normalizedLicenseType", "@{normalizedLicenseType}" )
+ .withSetting("normalizedLicense", "@{normalizedLicense}" )
+ .withSetting("normalizedLicenseUrl", $licUrl, "taking url from read data" )
+ .withSetting("normalizedLicenseUrl", "@{normalizedLicenseUrl}" )
+ .withSetting("comment", "@{comment}")
+ .nop();
+ ModelHelper.appendTraceToNormalizedLicense(normalizedLicense,aeb.build());
+
+ insert(normalizedLicense);
+
+end
+
+end template
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql
new file mode 100644
index 00000000..342c69a7
--- /dev/null
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql
@@ -0,0 +1,26 @@
+-- SPDX-License-Identifier: Apache-2.0
+--
+select
+ GROUP_CONCAT(DISTINCT a."applicationName" ORDER BY "applicationName" DESC SEPARATOR ', ') as APPS,
+ GROUP_CONCAT(DISTINCT ac."version" ORDER BY "version" DESC SEPARATOR ', ') as "version" ,
+ ac."groupId",
+ ac."artifactId",
+ ac."ossHomepage",
+ ac."copyrights"
+from
+ APPLICATION a,
+ APPLICATIONCOMPONENT ac,
+ NORMALIZEDLICENSE l
+where
+ a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND
+ ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND
+ l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE')
+group by
+ "groupId",
+ "artifactId",
+ "ossHomepage",
+ "copyrights"
+order by
+ "groupId",
+ "artifactId",
+ "version"
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql
new file mode 100644
index 00000000..d020df5d
--- /dev/null
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql
@@ -0,0 +1,31 @@
+-- SPDX-License-Identifier: Apache-2.0
+--
+select
+ GROUP_CONCAT(DISTINCT a."applicationName" ORDER BY "applicationName" DESC SEPARATOR ', ') as APPS,
+ GROUP_CONCAT(DISTINCT ac."version" ORDER BY "version" DESC SEPARATOR ', ') as "version" ,
+ ac."groupId",
+ ac."artifactId",
+ ac."ossHomepage",
+ ac."copyrights",
+ l."normalizedLicense",
+ l."effectiveNormalizedLicense"
+from
+ APPLICATION a,
+ APPLICATIONCOMPONENT ac,
+ NORMALIZEDLICENSE l
+where
+ a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND
+ ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND
+ l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE')
+group by
+ "groupId",
+ "artifactId",
+ "ossHomepage",
+ "copyrights",
+ "normalizedLicense",
+ "effectiveNormalizedLicense"
+order by
+ "groupId",
+ "artifactId",
+ "version",
+ "normalizedLicense"
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql
new file mode 100644
index 00000000..83e09d89
--- /dev/null
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql
@@ -0,0 +1,20 @@
+-- SPDX-License-Identifier: Apache-2.0
+--
+-- returns all distinct notice file texts
+select distinct
+ GROUP_CONCAT(DISTINCT CONCAT( ac."artifactId", ' (', ac."version", ')' ) ORDER BY "artifactId" ASC, "version" ASC SEPARATOR ', ') as "artifact",
+ ARRAY_AGG(DISTINCT ac."noticeFileContent" ORDER BY "noticeFileContent" DESC)[1] as "noticeFileContent",
+ UCASE(REGEXP_REPLACE(ac."noticeFileContent",'\s','')) as "unifiedNoticeFileContent"
+from
+ APPLICATION a,
+ APPLICATIONCOMPONENT ac,
+ NORMALIZEDLICENSE l
+where
+ a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND
+ ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND
+ ac."noticeFileUrl" is not null AND
+ l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE')
+group by
+ "unifiedNoticeFileContent"
+order by
+ "unifiedNoticeFileContent"
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql
index c9cd8a8e..b9ff9f89 100644
--- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql
@@ -6,6 +6,7 @@ select
ac."groupId",
ac."artifactId",
ac."packageUrl",
+ ac."copyrights",
l."effectiveNormalizedLicense",
l."effectiveNormalizedLicenseUrl",
l."effectiveNormalizedLicenseContent",
@@ -20,8 +21,9 @@ where
l."effectiveNormalizedLicenseType" like 'OSS-%'
group by
"groupId",
- "artifactId",
+ "artifactId",
"packageUrl",
+ "copyrights",
"effectiveNormalizedLicense",
"effectiveNormalizedLicenseUrl",
"effectiveNormalizedLicenseContent",
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossden_normalizedlicenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossden_normalizedlicenses.sql
new file mode 100644
index 00000000..735e5406
--- /dev/null
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossden_normalizedlicenses.sql
@@ -0,0 +1,29 @@
+-- SPDX-License-Identifier: Apache-2.0
+--
+-- generate all NormalizedLicenses in denormalized form including all hierachical data (allden -> "all denormalized")
+select
+ CONCAT(NVL(a."applicationName",'-'),NVL(ac."groupId",'-'),NVL(ac."artifactId",'-'),NVL(ac."version",'-'),NVL(l."normalizedLicense",'-')) as CORR_KEY_0,
+ CONCAT(NVL(a."applicationName",'-'),NVL(ac."groupId",'-'),NVL(ac."artifactId",'-'),NVL(l."normalizedLicense",'-')) as CORR_KEY_1,
+ CONCAT(NVL(a."applicationName",'-'),NVL(ac."groupId",'-'),NVL(ac."artifactId",'-'),NVL(ac."version",'-')) as CORR_KEY_2,
+ CONCAT(NVL(a."applicationName",'-'),NVL(ac."groupId",'-'),NVL(ac."artifactId",'-')) as CORR_KEY_3,
+ e.*,
+ a.*,
+ ac.*,
+ l.*
+from
+ ENGAGEMENT e,
+ APPLICATION a,
+ APPLICATIONCOMPONENT ac,
+ NORMALIZEDLICENSE l
+where
+ e.ID_ENGAGEMENT = a.PARENT_APPLICATION AND
+ a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND
+ ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND
+ l."normalizedLicenseType" not in ('COMMERCIAL', 'IGNORE')
+order by
+ UPPER("ID_APPLICATION"), -- sort by ID so assuring we have the same order as defined in config
+ UPPER("groupId"),
+ UPPER("artifactId"),
+ UPPER("version"),
+ UPPER("effectiveNormalizedLicense"),
+ UPPER("normalizedLicense")
\ No newline at end of file
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/scancode_sources.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/scancode_sources.sql
index 1062b668..83cd8d15 100644
--- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/scancode_sources.sql
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/scancode_sources.sql
@@ -16,7 +16,8 @@ from
NORMALIZEDLICENSE l
where
a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND
- ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE
+ ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND
+ l."normalizedLicenseType" not in ('COMMERCIAL')
group by
"groupId",
"artifactId",
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql
new file mode 100644
index 00000000..61c91d4b
--- /dev/null
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql
@@ -0,0 +1,21 @@
+-- SPDX-License-Identifier: Apache-2.0
+--
+-- returns all distinct OSS-Licenses texts
+select distinct
+ GROUP_CONCAT(DISTINCT l."normalizedLicense" ORDER BY "normalizedLicense" ASC SEPARATOR ', ') as "normalizedLicense",
+ GROUP_CONCAT(DISTINCT CONCAT( ac."artifactId", ' (', ac."version", ')' ) ORDER BY "artifactId" ASC, "version" ASC SEPARATOR ', ') as "artifact",
+ ARRAY_AGG(DISTINCT l."normalizedLicenseContent" ORDER BY "normalizedLicenseContent" DESC)[1] as "normalizedLicenseContent",
+ UCASE(REGEXP_REPLACE(l."normalizedLicenseContent",'\s','')) as "unifiedNormalizedLicenseContent"
+from
+ APPLICATION a,
+ APPLICATIONCOMPONENT ac,
+ NORMALIZEDLICENSE l
+where
+ a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND
+ ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND
+ l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE') AND
+ l."normalizedLicenseUrl" is not null
+group by
+ "unifiedNormalizedLicenseContent"
+order by
+ "unifiedNormalizedLicenseContent"
diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm
new file mode 100644
index 00000000..3986dcc4
--- /dev/null
+++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm
@@ -0,0 +1,163 @@
+## template
+
+
+
+
+
+## styling (table)
+
+
+
+
+
+This document has 3 sections:
+Thirdparty Components
+THE FOLLOWING SETS FORTH ATTRIBUTION NOTICES FOR THIRD PARTY SOFTWARE THAT MAY BE CONTAINED IN PORTIONS OF THE $ENGAGEMENT.getDataRow(0).engagementName PRODUCT.
+
+
+
+Component / License overview
+
+The following table lists all third party Open Source Components that may be contained in this product. Besides name and version the table lists:
+
+
+
+
+
+ ## head row
+
+
+
+
+
+ ## content
+
+
+ #foreach ($license in $NONCOMMERCIALCOMPONENTS)
+ Name
+ Version
+ Source-Core-Repository
+ Licenses
+ Copyrights (or Authors/Contributors)
+
+ #set( $aid = $license.artifactId )
+ #set( $gid = $license.groupId )
+ #set( $ver = $license.version )
+ #set( $apps = $license.APPS )
+
+ #end
+
+$license.artifactId ## application component name
+ $license.version ## application component version
+ $license.ossHomepage
+
+ #foreach($ac in $NONCOMMERCIALCOMPONENTS_LICENSES )
+ #if( $aid == $ac.artifactId && $gid == $ac.groupId && $ver == $ac.version )
+## #set( $key = "${gid}${aid}${ac.normalizedLicense}" ) ## this is not necessarily sufficient as uinque id because version is missing
+## $ac.normalizedLicense ## linking does not work currently
+ $ac.normalizedLicense
+ #if( $ac.effectiveNormalizedLicense == "Ignore" && $ac.effectiveNormalizedLicense != $ac.normalizedLicense)
+ (NA)
+ #end
+
+
+ #end
+ #end
+ $esc.newLinesAsBreaks($esc.html($license.copyrights)) ## copyrights
+ License texts
+
+
+
+Note that not every license given or referenced in below texts necessarily applies in the
+context of the distribution of this product: See the table in Component /
+License overview for licenses which might not apply due to selected dual-/multi-licensing
+options.
+
+ #if ($nlc) +
+ $esc.html($esc.wrap100to80($nlc))+ |
+Defined/referenced license(s): $ul.normalizedLicense
+
+Applicable component(s): $ul.artifact
+
+Component(s): $nf.artifact +
++ #if ($nf.noticeFileContent) +
+ $esc.html($nf.noticeFileContent)+ |
+ * This reader requires a specific version of license-checker which is not released in the official npm repositories but is only
+ * available via Github. This might result in difficulties in environments which have only limited access to internet resources.
+ * Additionally, developer dependencies cannot be excluded as the --production option seemingly does not work properly.
+ * Use {@link NpmLicenseCheckerReader} instead.
*/
@Component
public class NpmLicenseCrawlerReader extends AbstractReader implements Reader {
@@ -62,7 +68,9 @@ public Set
- * This reader requires a specific version of license-checker which is not released in the official npm repositories but is only
- * available via Github. This might result in difficulties in environments which have only limited access to internet resources.
- * Additionally, developer dependencies cannot be excluded as the --production option seemingly does not work properly.
- * Use {@link NpmLicenseCheckerReader} instead.
+ * This reader requires a specific version of license-checker which is not released in the official npm repositories
+ * but is only available via Github. This might result in difficulties in environments which have only limited access to
+ * internet resources. Additionally, developer dependencies cannot be excluded as the --production option seemingly does
+ * not work properly. Use {@link NpmLicenseCheckerReader} instead.
*/
@Component
public class NpmLicenseCrawlerReader extends AbstractReader implements Reader {
@@ -68,9 +68,10 @@ public Set>.
+
+To install the NPM License Crawler the following command needs to be executed.
----
-npm i license-checker -g
+npm i npm-license-crawler -g
----
-To get the licenses, the checker needs to be executed like the following example (we require JSON output here)
+To get the licenses, the crawler needs to be executed like the following example
----
-license-checker --json > /path/to/licenses.json
+npm-license-crawler --dependencies --csv licenses.csv
----
-The export should look like the following
+The export should look like the following (The csv file is "," separated)
[source]
----
-include::files/licensesNpmLicenseChecker.json[]
+include::files/licenses.csv[]
----
-Source: https://www.npmjs.com/package/license-checker
+Source: https://www.npmjs.com/package/npm-license-crawler
In _Solicitor_ the data is read with the following part of the config
----
"readers" : [ {
- "type" : "npm-license-checker",
- "source" : "file:path/to/licenses.json",
+ "type" : "npm-license-crawler-csv",
+ "source" : "file:path/to/licenses.csv",
"usagePattern" : "DYNAMIC_LINKING",
"repoType" : "npm"
} ]
@@ -1246,8 +1248,9 @@ wrong/misleading output) the first stage of deprecation will be skipped.
=== List of Deprecated Features
The following features are deprecated via the above mechanism:
+* Reader of type "npm-license-crawler-csv" (use Reader of type "npm-license-checker" instead); Stage 1 from Version 1.4.0 on; see https://github.com/devonfw/solicitor/issues/125
* Reader of type "gradle" (use Reader of type "gradle2" instead); Stage 2 from Version 1.0.5 on; see https://github.com/devonfw/solicitor/issues/58
-* Reader of type "npm" (use type "npm-license-crawler-csv" instead); Stage 1 from Version 1.0.8 on; see https://github.com/devonfw/solicitor/issues/62
+* Reader of type "npm"; Stage 1 from Version 1.0.8 on; see https://github.com/devonfw/solicitor/issues/62
* "REGEX:" prefix notation in rule templates (use "(REGEX)" suffix instead); Stage 1 from Version 1.3.0 on; see https://github.com/devonfw/solicitor/issues/78
* Use of `LicenseAssignmentProject.xls` decision table (use `LicenseAssignmentV2Project.xls` instead); Stage 1 from Version 1.4.0 on
@@ -1486,6 +1489,8 @@ java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar
F)
z?4-2Zf&ExSJY+2*)&a#@WEfc_oVWov11DMJOYUNZi4q#ROZZq8963EjD0_$=GZi
z<=Y%R$1DfW&*zBcR#DISpJqPCFq!x$&dK-qB`j>_E8Y?i7KooJ;Sz0PiD6<1m>5sL
zc)<=Wf$Z?Es)|_+<
&r8lY1vh*)4bL{*J
zuGh8m*NJdbIe(of9$msEfPF^F>qN=E1x1)<4Q%HYfp9O69l53L+%oLkB0Kq>Cx3;G
zxLA3cl=(_|TPtrH%G<0|7H-ohjK0GbybQWSpm7RxM}zJdpgRN_uRwR;8|BGoPc3*P
zJVE?}Hnw*e>f(+6U4s5mLGNnlU4pKHdK~Dx1bwQbe3#aH%(C-e^V}oWb4qiMSik0y
zQ`|FL-Xma@0^ZYW`#u9*We)r`?tKE{Z_LreeIh)s@b`(ZS`qGR!UHBa%4tDAAi{ol
z8bjmuK=!9g^-!}O8mx!JdRs@)&BO_NQ~WB2
z*DX#~BRN|Fb=bE?f_
Bjw{0Sr
zKMY3_H(GVDeXpXe)qGLxZVI;IoetV-`CsvGV@A+^tC%)VUT7;+JcC#Gr#4uR;Nw5n
zVZs9IaG&mlI>?2cAJ%!vJ1#ivp_jcoAuV1bvtIJ<5A)4JY!qOn40}W{TsjDQq!aPT
zU_Bxht_6-l77oN)pdKN1Kz)agpor7A2laVaP
((4p7Qv77qWj@J?;KQ)#HA!-&OxPIx9Euj
zm6c`ee=d(n%b4mnm;Gzos-OH^aiYJtpX#fX({7$4p2y-(jd-3@fO$?D^U#dk*5->D
zzwqWey!p)g%<$&dBmkw6nHoXPNUG
z?4_ceHLT=NlRmJZA*pC*dE1oyx{xwUIQ7?=l%|*bE?l(N*+VmoqoTcTJ)AR2xdxtN
zWxzz8v-zBJ@;T?Q&RN!ZVb#h6oF|qKQVi_8g`Ia`=N;I2hS3b1|JNDd3AiAGfTVhX
z4X2thF4!?Hm@y_x(JnZHT(FKW3d}E#FET7tql*@H(J_2cSo*=&xZ{}1Q+J7lG+CEe
zbQQ7Es=wYZIVLVy6SN1zWl$m+U1nCX$>_2*aoI6(nOTKJHY{{UpiLsJDS=&KSSad>
zgnull
is returned if no data is available,
+ * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no
+ * data available no exception will be thrown. Instead null
will be returned in such a case.
+ */
+ ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException;
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapterException.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapterException.java
new file mode 100644
index 00000000..e1e91a4a
--- /dev/null
+++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapterException.java
@@ -0,0 +1,38 @@
+package com.devonfw.tools.solicitor.componentinfo;
+
+/**
+ * Exception indicating that the {@link ComponentInfoAdapter} had a problem reading data. This should not be used if
+ * there is simply no data available for the given component but only if there was either a general problem reading data
+ * or with reading the (existing) specific data for the component.
+ */
+public class ComponentInfoAdapterException extends Exception {
+
+ /**
+ * A constructor.
+ */
+ public ComponentInfoAdapterException() {
+
+ }
+
+ /**
+ * A constructor.
+ *
+ * @param message the message of the exception
+ */
+ public ComponentInfoAdapterException(String message) {
+
+ super(message);
+ }
+
+ /**
+ * A constructor.
+ *
+ * @param message the message of the exception
+ * @param cause the underlying {@link Throwable}.
+ */
+ public ComponentInfoAdapterException(String message, Throwable cause) {
+
+ super(message, cause);
+ }
+
+}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java
new file mode 100644
index 00000000..ae50de19
--- /dev/null
+++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java
@@ -0,0 +1,166 @@
+package com.devonfw.tools.solicitor.componentinfo;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import com.devonfw.tools.solicitor.InventoryProcessor;
+import com.devonfw.tools.solicitor.common.LogMessages;
+import com.devonfw.tools.solicitor.common.SolicitorRuntimeException;
+import com.devonfw.tools.solicitor.model.ModelFactory;
+import com.devonfw.tools.solicitor.model.ModelRoot;
+import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent;
+import com.devonfw.tools.solicitor.model.inventory.RawLicense;
+import com.devonfw.tools.solicitor.model.masterdata.Application;
+
+/**
+ * An {@link InventoryProcessor} which looks up license information for the found application components / packages at
+ * some external data source , like a scancode file store. If license information is found then the license, copyright
+ * and notice file information of the model will be replaced by the data obtained from this source.
+ *
+ */
+@Component
+@Order(InventoryProcessor.BEFORE_RULE_ENGINE)
+public class ComponentInfoInventoryProcessor implements InventoryProcessor {
+
+ private static class Statistics {
+ public int componentsTotal;
+
+ public int componentsWithComponentInfo;
+
+ public void add(Statistics statistics) {
+
+ this.componentsTotal += statistics.componentsTotal;
+ this.componentsWithComponentInfo += statistics.componentsWithComponentInfo;
+ }
+ }
+
+ /**
+ * Origin data for raw license objects created by this Class. Due to compatibility reasons this is named "scancode"
+ * even due to the fact that it might originate from other sources.
+ */
+ private static final String ORIGIN_COMPONENTINFO = "scancode";
+
+ private static final Logger LOG = LoggerFactory.getLogger(ComponentInfoInventoryProcessor.class);
+
+ private ComponentInfoAdapter[] componentInfoAdapters;
+
+ private ModelFactory modelFactory;
+
+ /**
+ * The constructor.
+ */
+ public ComponentInfoInventoryProcessor() {
+
+ }
+
+ @Override
+ public void processInventory(ModelRoot modelRoot) {
+
+ Statistics overall = new Statistics();
+ for (Application application : modelRoot.getEngagement().getApplications()) {
+ for (ApplicationComponent ac : application.getApplicationComponents()) {
+ Statistics single = processApplicationComponent(ac);
+ overall.add(single);
+ }
+ }
+ LOG.info(LogMessages.COMPONENT_INFO_READ.msg(), overall.componentsWithComponentInfo, overall.componentsTotal);
+ }
+
+ /**
+ * Process a single {@link ApplicationComponent}.
+ *
+ * @param ac application component
+ * @return processing statistics
+ */
+ private Statistics processApplicationComponent(ApplicationComponent ac) {
+
+ Statistics statistics = new Statistics();
+ statistics.componentsTotal = 1;
+
+ if (ac.getPackageUrl() != null) {
+ ComponentInfo componentInfo = null;
+ try {
+ for (ComponentInfoAdapter cia : this.componentInfoAdapters) {
+ componentInfo = cia.getComponentInfo(ac.getPackageUrl());
+ // stop querying further adapters if some info was returned
+ if (componentInfo != null) {
+ break;
+ }
+ }
+ } catch (ComponentInfoAdapterException e) {
+ throw new SolicitorRuntimeException("Exception when reading component info data source", e);
+ }
+ if (componentInfo != null) {
+ statistics.componentsWithComponentInfo = 1;
+ ac.removeAllRawLicenses();
+
+ if (componentInfo.getNoticeFilePath() != null) {
+ ac.setNoticeFileUrl(componentInfo.getNoticeFilePath());
+ }
+
+ for (LicenseInfo li : componentInfo.getLicenses()) {
+ addRawLicense(ac, li.getSpdxid(), li.getLicenseFilePath(), ORIGIN_COMPONENTINFO);
+ }
+ String copyrights = String.join("\n", componentInfo.getCopyrights());
+ ac.setCopyrights(copyrights);
+ // check whether VendorUrl is included in input file or not
+ if (componentInfo.getUrl() != null) {
+ ac.setOssHomepage(componentInfo.getUrl());
+ }
+ } else {
+ // no ComponentInfos info found for ac
+ }
+ } else {
+ // can this happen?
+ }
+ return statistics;
+ }
+
+ /**
+ * Adds a {@link com.devonfw.tools.solicitor.model.inventory.RawLicense} to the given
+ * {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent}.
+ *
+ * @param appComponent a {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent} object.
+ * @param name a {@link java.lang.String} object.
+ * @param url a {@link java.lang.String} object.
+ * @param origin a {@link java.lang.String} object.
+ */
+ public void addRawLicense(ApplicationComponent appComponent, String name, String url, String origin) {
+
+ RawLicense mlic = this.modelFactory.newRawLicense();
+ mlic.setApplicationComponent(appComponent);
+ mlic.setDeclaredLicense(name);
+ mlic.setLicenseUrl(url);
+ mlic.setOrigin(origin);
+ String trace;
+ trace = "+ Component/License info read from ComponentInfo data source";
+
+ mlic.setTrace(trace);
+ }
+
+ /**
+ * This method sets the field modelFactory
.
+ *
+ * @param modelFactory the new value of the field modelFactory
+ */
+ @Autowired
+ public void setModelFactory(ModelFactory modelFactory) {
+
+ this.modelFactory = modelFactory;
+ }
+
+ /**
+ * This method sets the field componentInfoAdapters
+ *
+ * @param componentInfoAdapters new value of componentInfoAdapters
.
+ */
+ @Autowired
+ public void setComponentInfoAdapters(ComponentInfoAdapter[] componentInfoAdapters) {
+
+ this.componentInfoAdapters = componentInfoAdapters;
+ }
+
+}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java
new file mode 100644
index 00000000..5f76694b
--- /dev/null
+++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java
@@ -0,0 +1,19 @@
+package com.devonfw.tools.solicitor.componentinfo;
+
+/**
+ * License information within the {@link ComponentInfo} data structure.
+ *
+ */
+public interface LicenseInfo {
+
+ /**
+ * @return spdxid
+ */
+ String getSpdxid();
+
+ /**
+ * @return licenseFilePath
+ */
+ String getLicenseFilePath();
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java
similarity index 59%
rename from core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java
rename to core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java
index 7c6a0f89..97a966db 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java
@@ -1,47 +1,49 @@
-package com.devonfw.tools.solicitor.scancode;
+package com.devonfw.tools.solicitor.componentinfo.scancode;
import java.util.Collection;
import java.util.Collections;
-import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
+import com.devonfw.tools.solicitor.componentinfo.ComponentInfo;
+import com.devonfw.tools.solicitor.componentinfo.LicenseInfo;
+
/**
* Data structure for holding the scan information from scancode for a single package.
*/
-public class ComponentScancodeInfos {
+public class ScancodeComponentInfo implements ComponentInfo {
/**
* Holds the info about a single found license.
*/
- public class LicenseInfo {
+ public class ScancodeLicenseInfo implements LicenseInfo {
/**
* Id for of the license.
*/
- public String id;
+ private String id;
/**
* SPDX-ID of the license.
*/
- public String spdxid;
+ private String spdxid;
/**
* The score of the license.
*/
- public double licenseScore;
+ private double licenseScore;
/**
* Path to the license file.
*/
- public String licenseFilePath;
+ private String licenseFilePath;
/**
* The score of the license file.
*/
- public double licenseFileScore;
+ private double licenseFileScore;
/**
* The constructor.
@@ -53,14 +55,14 @@ public class LicenseInfo {
* @param licenseFilePath the path to the license file
* @param licenseFileScore the score of the license file
*/
- public LicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseFilePath,
+ public ScancodeLicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseFilePath,
double licenseFileScore) {
super();
this.id = id;
this.spdxid = spdxid;
this.licenseScore = licenseScore;
- if (licenseFileScore >= ComponentScancodeInfos.this.minLicensefilePercentage) {
+ if (licenseFileScore >= ScancodeComponentInfo.this.minLicensefilePercentage) {
this.licenseFilePath = licenseFilePath;
this.licenseFileScore = licenseFileScore;
} else {
@@ -68,13 +70,95 @@ public LicenseInfo(String id, String spdxid, String defaultUrl, double licenseSc
this.licenseFileScore = 0.0;
}
}
+
+ /**
+ * @return id
+ */
+ public String getId() {
+
+ return this.id;
+ }
+
+ /**
+ * @return spdxid
+ */
+ @Override
+ public String getSpdxid() {
+
+ return this.spdxid;
+ }
+
+ /**
+ * @return licenseScore
+ */
+ public double getLicenseScore() {
+
+ return this.licenseScore;
+ }
+
+ /**
+ * @return licenseFilePath
+ */
+ @Override
+ public String getLicenseFilePath() {
+
+ return this.licenseFilePath;
+ }
+
+ /**
+ * @return licenseFileScore
+ */
+ public double getLicenseFileScore() {
+
+ return this.licenseFileScore;
+ }
+
+ /**
+ * @param id new value of {@link #getId}.
+ */
+ public void setId(String id) {
+
+ this.id = id;
+ }
+
+ /**
+ * @param spdxid new value of {@link #getSpdxid}.
+ */
+ public void setSpdxid(String spdxid) {
+
+ this.spdxid = spdxid;
+ }
+
+ /**
+ * @param licenseScore new value of {@link #getLicenseScore}.
+ */
+ public void setLicenseScore(double licenseScore) {
+
+ this.licenseScore = licenseScore;
+ }
+
+ /**
+ * @param licenseFilePath new value of {@link #getLicenseFilePath}.
+ */
+ public void setLicenseFilePath(String licenseFilePath) {
+
+ this.licenseFilePath = licenseFilePath;
+ }
+
+ /**
+ * @param licenseFileScore new value of {@link #getLicenseFileScore}.
+ */
+ public void setLicenseFileScore(double licenseFileScore) {
+
+ this.licenseFileScore = licenseFileScore;
+ }
}
private static final double MIN_NOTICEFILE_PERCENTAGE = 0.0;
private SortedSetnull
is
* returned if no data is available,
- * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available
- * no exception will be thrown. Instead null
will be return in such a case.
+ * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no
+ * data available no exception will be thrown. Instead null
will be return in such a case.
*/
@Override
- public ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throws ScancodeException {
+ public ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException {
- ComponentScancodeInfos componentScancodeInfos = determineScancodeInformation(packageUrl);
- if (componentScancodeInfos == null) {
+ if (isFeatureActive()) {
+
+ ScancodeComponentInfo componentScancodeInfos = determineScancodeInformation(packageUrl);
+ if (componentScancodeInfos == null) {
+ return null;
+ }
+ applyCurations(packageUrl, componentScancodeInfos);
+
+ return componentScancodeInfos;
+
+ } else {
return null;
}
- applyCurations(packageUrl, componentScancodeInfos);
- return componentScancodeInfos;
+ }
+ private boolean isFeatureActive() {
+
+ if (!this.featureLogged) {
+ if (this.featureFlag) {
+ LOG.warn(LogMessages.SCANCODE_PROCESSOR_STARTING.msg());
+ } else {
+ LOG.info(LogMessages.SCANCODE_FEATURE_DEACTIVATED.msg());
+ }
+ this.featureLogged = true;
+ }
+ return this.featureFlag;
}
/**
@@ -124,12 +163,12 @@ public ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throw
*
* @param packageUrl The package url of the package
* @return the read scancode information, null
if no information was found
- * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available
- * no exception will be thrown. Instead null
will be return in such a case.
+ * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no
+ * data available no exception will be thrown. Instead null
will be return in such a case.
*/
- private ComponentScancodeInfos determineScancodeInformation(String packageUrl) throws ScancodeException {
+ private ScancodeComponentInfo determineScancodeInformation(String packageUrl) throws ComponentInfoAdapterException {
- ComponentScancodeInfos componentScancodeInfos = new ComponentScancodeInfos(this.minLicenseScore,
+ ScancodeComponentInfo componentScancodeInfos = new ScancodeComponentInfo(this.minLicenseScore,
this.minLicensefilePercentage);
String packagePathPart = this.packageURLHandler.pathFor(packageUrl);
String path = this.repoBasePath + "/" + packagePathPart + "/scancode.json";
@@ -153,10 +192,10 @@ private ComponentScancodeInfos determineScancodeInformation(String packageUrl) t
+ this.packageURLHandler.pathFor(packageUrl) + "/" + file.get("path").asText(), 100.0);
}
for (JsonNode cr : file.get("copyrights")) {
- if(cr.has("copyright")) {
+ if (cr.has("copyright")) {
componentScancodeInfos.addCopyright(cr.get("copyright").asText());
} else {
- componentScancodeInfos.addCopyright(cr.get("value").asText());
+ componentScancodeInfos.addCopyright(cr.get("value").asText());
}
}
@@ -178,7 +217,7 @@ private ComponentScancodeInfos determineScancodeInformation(String packageUrl) t
componentScancodeInfos.getNoticeFilePath() != null ? 1 : 0);
} catch (IOException e) {
- throw new ScancodeException("Could not read Scancode JSON", e);
+ throw new ComponentInfoAdapterException("Could not read Scancode JSON", e);
}
return componentScancodeInfos;
}
@@ -189,10 +228,10 @@ private ComponentScancodeInfos determineScancodeInformation(String packageUrl) t
*
* @param packageUrl the identifier of the package
* @param componentScancodeInfos the componentScancodeInfos to curate
- * @throws ScancodeException if the curations could not be read
+ * @throws ComponentInfoAdapterException if the curations could not be read
*/
- private void applyCurations(String packageUrl, ComponentScancodeInfos componentScancodeInfos)
- throws ScancodeException {
+ private void applyCurations(String packageUrl, ScancodeComponentInfo componentScancodeInfos)
+ throws ComponentInfoAdapterException {
String packagePathPart = this.packageURLHandler.pathFor(packageUrl);
@@ -216,7 +255,7 @@ private void applyCurations(String packageUrl, ComponentScancodeInfos componentS
for (JsonNode curations : curationsObj.get("artifacts")) {
String component = curations.get("name").asText();
if (component.equals(packagePathPart)) {
- ComponentScancodeInfos oneComponent = componentScancodeInfos;
+ ScancodeComponentInfo oneComponent = componentScancodeInfos;
if (curations.get("copyrights") != null) {
oneComponent.clearCopyrights();
int authorCount = curations.get("copyrights").size();
@@ -240,7 +279,7 @@ private void applyCurations(String packageUrl, ComponentScancodeInfos componentS
}
} catch (IOException e) {
- throw new ScancodeException("Could not read Curations JSON", e);
+ throw new ComponentInfoAdapterException("Could not read Curations JSON", e);
}
}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java
deleted file mode 100644
index 997b9926..00000000
--- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.devonfw.tools.solicitor.scancode;
-
-/**
- * Adapter for reading Scancode information for a package, applying any given curations and returning the information as
- * a {@link ComponentScancodeInfos} object.
- */
-public interface ScancodeAdapter {
-
- /**
- * Retrieves the Scancode information and curations for a package identified by the given package URL. Returns the
- * data as a {@link ComponentScancodeInfos} object.
- *
- * @param packageUrl The identifier of the package for which information is requested
- * @return the data derived from the scancode results after applying any defined curations. null
is
- * returned if no data is available,
- * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available
- * no exception will be thrown. Instead null
will be return in such a case.
- */
- ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throws ScancodeException;
-
-}
\ No newline at end of file
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java
deleted file mode 100644
index 9b9eb761..00000000
--- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.devonfw.tools.solicitor.scancode;
-
-/**
- * Exception indicating that the Scancode information could not be read.
- */
-public class ScancodeException extends Exception {
-
- /**
- * A constructor.
- */
- public ScancodeException() {
-
- }
-
- /**
- * A constructor.
- *
- * @param message the message of the exception
- */
- public ScancodeException(String message) {
-
- super(message);
- }
-
- /**
- * A constructor.
- *
- * @param message the message of the exception
- * @param cause the underlying {@link Throwable}.
- */
- public ScancodeException(String message, Throwable cause) {
-
- super(message, cause);
- }
-
-}
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java
deleted file mode 100644
index 9f2edece..00000000
--- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java
+++ /dev/null
@@ -1,193 +0,0 @@
-package com.devonfw.tools.solicitor.scancode;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Component;
-
-import com.devonfw.tools.solicitor.InventoryProcessor;
-import com.devonfw.tools.solicitor.common.LogMessages;
-import com.devonfw.tools.solicitor.common.SolicitorRuntimeException;
-import com.devonfw.tools.solicitor.model.ModelFactory;
-import com.devonfw.tools.solicitor.model.ModelRoot;
-import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent;
-import com.devonfw.tools.solicitor.model.inventory.RawLicense;
-import com.devonfw.tools.solicitor.model.masterdata.Application;
-import com.devonfw.tools.solicitor.scancode.ComponentScancodeInfos.LicenseInfo;
-
-/**
- * An {@link InventoryProcessor} which looks up license information for the found application components / packages in
- * the scancode file store. If license information is found then the license, copyright and notice file information of
- * the model will be replaced by the data obtained from scancode.
- *
- */
-@Component
-@Order(InventoryProcessor.BEFORE_RULE_ENGINE)
-public class ScancodeInventoryProcessor implements InventoryProcessor {
-
- private static class Statistics {
- public int componentsTotal;
-
- public int componentsWithScancodeInfos;
-
- public void add(Statistics statistics) {
-
- this.componentsTotal += statistics.componentsTotal;
- this.componentsWithScancodeInfos += statistics.componentsWithScancodeInfos;
- }
- }
-
- /**
- * Origin data for raw license objects created by this Class.
- */
- private static final String ORIGIN_SCANCODE = "scancode";
-
- private static final Logger LOG = LoggerFactory.getLogger(ScancodeInventoryProcessor.class);
-
- private boolean featureFlag = false;
-
- private ScancodeAdapter scancodeAdapter;
-
- private ModelFactory modelFactory;
-
- /**
- * Sets the feature flag for activating/deactivating this feature.
- *
- * @param featureFlag the flag
- */
- @Value("${solicitor.feature-flag.scancode}")
- public void setFeatureFlag(boolean featureFlag) {
-
- this.featureFlag = featureFlag;
- }
-
- /**
- * The constructor.
- */
- public ScancodeInventoryProcessor() {
-
- }
-
- @Override
- public void processInventory(ModelRoot modelRoot) {
-
- if (!this.featureFlag) {
- LOG.info(LogMessages.SCANCODE_FEATURE_DEACTIVATED.msg());
- } else {
- LOG.warn(LogMessages.SCANCODE_PROCESSOR_STARTING.msg());
-
- Statistics overall = new Statistics();
- for (Application application : modelRoot.getEngagement().getApplications()) {
- for (ApplicationComponent ac : application.getApplicationComponents()) {
- Statistics single = processApplicationComponent(ac);
- overall.add(single);
- }
- }
- LOG.info(LogMessages.SCANCODE_INFO_READ.msg(), overall.componentsWithScancodeInfos, overall.componentsTotal);
- }
- }
-
- /**
- * Process a single {@link ApplicationComponent}.
- *
- * @param ac application component
- * @return processing statistics
- */
- private Statistics processApplicationComponent(ApplicationComponent ac) {
-
- Statistics statistics = new Statistics();
- statistics.componentsTotal = 1;
-
- if (ac.getPackageUrl() != null) {
- ComponentScancodeInfos componentScancodeInfos;
- try {
- componentScancodeInfos = this.scancodeAdapter.getComponentScancodeInfos(ac.getPackageUrl());
- } catch (ScancodeException e) {
- throw new SolicitorRuntimeException("Exception when reading scancode file", e);
- }
- if (componentScancodeInfos != null) {
- statistics.componentsWithScancodeInfos = 1;
- ac.removeAllRawLicenses();
-
- if (componentScancodeInfos.getNoticeFilePath() != null) {
- ac.setNoticeFileUrl(componentScancodeInfos.getNoticeFilePath());
- }
-
- for (LicenseInfo li : componentScancodeInfos.getLicenses().values()) {
- addRawLicense(ac, li.spdxid, li.licenseFilePath, ORIGIN_SCANCODE);
- }
- String copyrights = String.join("\n", componentScancodeInfos.getCopyrights());
- ac.setCopyrights(copyrights);
- // check whether VendorUrl is included in input file or not
- if (componentScancodeInfos.getUrl() != null) {
- ac.setOssHomepage(componentScancodeInfos.getUrl());
- }
- } else {
- // no scancode info found for ac
- }
- } else {
- // can this happen?
- }
- return statistics;
- }
-
- /**
- * Performs logging.
- *
- * @param sourceUrl the URL from where the inventory data was read
- * @param application the application
- * @param readComponents number of read ApplicationComponents
- * @param readLicenses number of read RawLicenses
- */
- public void doLogging(String sourceUrl, Application application, int readComponents, int readLicenses) {
-
- LOG.info(LogMessages.READING_INVENTORY.msg(), readComponents, readLicenses, application.getName(), sourceUrl);
- }
-
- /**
- * Adds a {@link com.devonfw.tools.solicitor.model.inventory.RawLicense} to the given
- * {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent}.
- *
- * @param appComponent a {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent} object.
- * @param name a {@link java.lang.String} object.
- * @param url a {@link java.lang.String} object.
- * @param origin a {@link java.lang.String} object.
- */
- public void addRawLicense(ApplicationComponent appComponent, String name, String url, String origin) {
-
- RawLicense mlic = this.modelFactory.newRawLicense();
- mlic.setApplicationComponent(appComponent);
- mlic.setDeclaredLicense(name);
- mlic.setLicenseUrl(url);
- mlic.setOrigin(origin);
- String trace;
- trace = "+ Component/License info read from scancode information";
-
- mlic.setTrace(trace);
- }
-
- /**
- * This method sets the field modelFactory
.
- *
- * @param modelFactory the new value of the field modelFactory
- */
- @Autowired
- public void setModelFactory(ModelFactory modelFactory) {
-
- this.modelFactory = modelFactory;
- }
-
- /**
- * This method sets the field scancodeAdapter
- *
- * @param scancodeAdapter new value of scancodeAdapter
.
- */
- @Autowired
- public void setScancodeAdapter(ScancodeAdapter scancodeAdapter) {
-
- this.scancodeAdapter = scancodeAdapter;
- }
-
-}
diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc
index 07d8b092..7bd1b9f9 100644
--- a/documentation/master-solicitor.asciidoc
+++ b/documentation/master-solicitor.asciidoc
@@ -1528,11 +1528,31 @@ The Extension will be activated by referencing it as follows when starting _Soli
[listing]
java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar sourceRepoUrl
.
+ *
+ * @return the field sourceRepoUrl
+ */
+ String getSourceRepoUrl();
+
/**
* This method gets the field noticeFileUrl
.
*
@@ -154,11 +161,18 @@ public interface ApplicationComponent {
void setOssHomepage(String ossHomepage);
/**
- * This method sets the field noticeFileUrl
.
+ * This method sets the field ossHomepage
.
+ *
+ * @param ossHomepage the new value of the field ossHomepage
+ */
+ void setSourceRepoUrl(String ossHomepage);
+
+ /**
+ * This method sets the field sourceRepoUrl
.
*
- * @param noticeFileUrl the new value of the field noticeFileUrl
+ * @param sourceRepoUrl the new value of the field sourceRepoUrl
*/
- void setNoticeFileUrl(String noticeFileUrl);
+ void setNoticeFileUrl(String sourceRepoUrl);
/**
* This method sets the field ossModified
.
diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java
index 7cb1ec95..c5cc002d 100644
--- a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java
+++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java
@@ -65,9 +65,6 @@ public void readInventory(String type, String sourceUrl, Application application
String licenseFile = (String) attributes.get("licenseFile");
String licenseUrl = estimateLicenseUrl(repo, path, licenseFile);
String homePage = (String) attributes.get("url");
- if (homePage == null || homePage.isEmpty()) {
- homePage = repo;
- }
Object lic = attributes.get("licenses");
List
@@ -75,7 +75,7 @@ The following table lists all third party Open Source Components that may be con
#set( $apps = $license.APPS )
Name
Version
- Source-Core-Repository
+ Source Code Repository
Licenses
Copyrights (or Authors/Contributors)
$license.artifactId ## application component name
$license.version ## application component version
- $license.ossHomepage
+ $license.sourceRepoUrl
#foreach($ac in $NONCOMMERCIALCOMPONENTS_LICENSES )
#if( $aid == $ac.artifactId && $gid == $ac.groupId && $ver == $ac.version )
@@ -104,9 +104,9 @@ This section lists all license texts that might be applicable for the third part
g2If?p0IS
zYK<;IpMt{YQnH|go|sh)sj-H^t06r0Lf+Dl2o>IdU`i#~*izxpELp0i^JrTvr2X}_
z