Skip to content

Commit 6ec7927

Browse files
committed
AE-1698: aggregate license / components folders
1 parent 6cbb5ac commit 6ec7927

File tree

24 files changed

+385
-0
lines changed

24 files changed

+385
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Copyright 2009-2026 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.metaeffekt.core.inventory.processor.report;
17+
18+
import lombok.extern.slf4j.Slf4j;
19+
import org.apache.commons.lang3.StringUtils;
20+
import org.apache.tools.ant.Project;
21+
import org.apache.tools.ant.taskdefs.Copy;
22+
import org.apache.tools.ant.types.FileSet;
23+
import org.metaeffekt.core.inventory.processor.model.Artifact;
24+
import org.metaeffekt.core.inventory.processor.model.Constants;
25+
import org.metaeffekt.core.inventory.processor.model.Inventory;
26+
import org.metaeffekt.core.inventory.processor.model.LicenseMetaData;
27+
import org.metaeffekt.core.inventory.processor.report.configuration.ReportConfigurationParameters;
28+
29+
import java.io.File;
30+
import java.util.HashSet;
31+
import java.util.Set;
32+
33+
import static org.metaeffekt.core.inventory.processor.model.Constants.*;
34+
35+
@Slf4j
36+
public class AnnexResourceProcessor {
37+
private final Inventory inventory;
38+
private final Inventory referenceInventory;
39+
private final ReportConfigurationParameters configParams;
40+
41+
private final File referenceInventoryDir;
42+
private final String referenceComponentPath;
43+
private final String referenceLicensePath;
44+
45+
private final File targetComponentDir;
46+
private final File targetLicenseDir;
47+
48+
public AnnexResourceProcessor(Inventory inventory, Inventory referenceInventory,
49+
ReportConfigurationParameters configParams,
50+
File referenceInventoryDir, String referenceComponentPath, String referenceLicensePath,
51+
File targetComponentDir, File targetLicenseDir) {
52+
this.inventory = inventory;
53+
this.referenceInventory = referenceInventory;
54+
this.configParams = configParams;
55+
this.referenceInventoryDir = referenceInventoryDir;
56+
this.referenceComponentPath = referenceComponentPath;
57+
this.referenceLicensePath = referenceLicensePath;
58+
this.targetComponentDir = targetComponentDir;
59+
this.targetLicenseDir = targetLicenseDir;
60+
}
61+
62+
public boolean execute() {
63+
// Enrich the local inventory with reference data
64+
enrichInventory();
65+
66+
// Perform folder creation
67+
final Set<String> reportedSourceFolders = new HashSet<>();
68+
boolean missingFiles = false;
69+
70+
for (Artifact artifact : inventory.getArtifacts()) {
71+
if (!artifact.isEnabledForDistribution()) {
72+
continue;
73+
}
74+
75+
final String componentName = artifact.getComponent();
76+
final String sourceLicense = artifact.getLicense();
77+
78+
if (StringUtils.isBlank(sourceLicense) || StringUtils.isBlank(componentName)) {
79+
continue;
80+
}
81+
82+
final String version = artifact.getVersion();
83+
boolean isArtifactVersionWildcard = isWildcard(version) ||
84+
STRING_TRUE.equalsIgnoreCase(artifact.get(Constants.KEY_WILDCARD_MATCH));
85+
86+
boolean isUndefinedVersion = version == null;
87+
88+
// Resolve license metadata from the now-enriched inventory
89+
final LicenseMetaData matchingLicenseMetaData = inventory.
90+
findMatchingLicenseMetaData(componentName, sourceLicense, version);
91+
92+
String effectiveLicense = artifact.getLicense();
93+
if (matchingLicenseMetaData != null) {
94+
effectiveLicense = matchingLicenseMetaData.deriveLicenseInEffect();
95+
}
96+
// Normalize license string for folder splitting
97+
effectiveLicense = effectiveLicense.replaceAll("\\s*,\\s*", "|");
98+
99+
final String versionUnspecificComponentFolder = LicenseMetaData.deriveComponentFolderName(componentName);
100+
final String versionSpecificComponentFolder = LicenseMetaData.deriveComponentFolderName(componentName, version);
101+
102+
final String sourcePath = (isArtifactVersionWildcard || isUndefinedVersion) ?
103+
versionUnspecificComponentFolder : versionSpecificComponentFolder;
104+
105+
final String targetPath = (isArtifactVersionWildcard || isUndefinedVersion) ?
106+
versionUnspecificComponentFolder : versionSpecificComponentFolder;
107+
108+
// Copy logic
109+
if (targetComponentDir != null) {
110+
missingFiles |= checkAndCopyComponentFolder(sourcePath,
111+
new File(targetComponentDir, targetPath), reportedSourceFolders);
112+
}
113+
114+
if (targetLicenseDir != null) {
115+
for (String licenseInEffect : effectiveLicense.split("\\|")) {
116+
final String licenseFolderName = LicenseMetaData.deriveLicenseFolderName(licenseInEffect);
117+
File licenseTargetDir = new File(targetLicenseDir, licenseFolderName);
118+
119+
missingFiles |= checkAndCopyLicenseFolder(licenseFolderName,
120+
licenseTargetDir, reportedSourceFolders);
121+
122+
missingFiles |= checkAndCopyComponentFolder(sourcePath,
123+
new File(licenseTargetDir, targetPath), reportedSourceFolders);
124+
}
125+
}
126+
}
127+
return !missingFiles;
128+
}
129+
130+
private void enrichInventory() {
131+
if (referenceInventory != null) {
132+
log.debug("Enriching inventory with reference inventory data.");
133+
inventory.inheritLicenseMetaData(referenceInventory, false);
134+
inventory.filterLicenseMetaData();
135+
inventory.inheritLicenseData(referenceInventory, false);
136+
}
137+
}
138+
139+
private boolean isWildcard(String version) {
140+
return ASTERISK.equalsIgnoreCase(version) ||
141+
(version != null && version.startsWith(VERSION_PLACHOLDER_PREFIX) && version.endsWith(VERSION_PLACHOLDER_SUFFIX));
142+
}
143+
144+
private boolean checkAndCopyLicenseFolder(String folderName, File targetDir, Set<String> reported) {
145+
File sourceDir = new File(referenceInventoryDir, referenceLicensePath);
146+
return performCopy(sourceDir, folderName, targetDir, reported, configParams.isFailOnMissingLicenseFile(), "[missing license file]");
147+
}
148+
149+
private boolean checkAndCopyComponentFolder(String folderName, File targetDir, Set<String> reported) {
150+
File sourceDir = new File(referenceInventoryDir, referenceComponentPath);
151+
return performCopy(sourceDir, folderName, targetDir, reported, configParams.isFailOnMissingComponentFiles(), "[missing component specific license file]");
152+
}
153+
154+
private boolean performCopy(File sourceRootDir, String folderName, File targetDir, Set<String> reported, boolean failFlag, String errorMsg) {
155+
File sourceFolder = new File(sourceRootDir, folderName);
156+
if (sourceFolder.exists()) {
157+
copyFolderContent(sourceRootDir, folderName, targetDir);
158+
return false;
159+
} else {
160+
if (!reported.contains(folderName)) {
161+
if (failFlag) {
162+
log.error("{} in folder [{}]", errorMsg, folderName);
163+
} else if (reported.size() <= 10) {
164+
log.warn("{} in folder [{}]", errorMsg, folderName);
165+
}
166+
reported.add(folderName);
167+
return true;
168+
}
169+
return false;
170+
}
171+
}
172+
173+
private void copyFolderContent(File sourceRootDir, String sourcePath, File targetDir) {
174+
log.info("copied {} from {} to {}", sourceRootDir, sourcePath, targetDir);
175+
Copy copy = new Copy();
176+
copy.setProject(new Project());
177+
FileSet fileSet = new FileSet();
178+
fileSet.setDir(new File(sourceRootDir, sourcePath));
179+
fileSet.setIncludes("**/*");
180+
copy.setIncludeEmptyDirs(false);
181+
copy.setFailOnError(true);
182+
copy.setOverwrite(true);
183+
copy.addFileset(fileSet);
184+
copy.setTodir(targetDir);
185+
copy.perform();
186+
}
187+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2009-2026 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.metaeffekt.core.inventory.processor.report;
17+
18+
import org.junit.Test;
19+
import org.metaeffekt.core.inventory.processor.model.Inventory;
20+
import org.metaeffekt.core.inventory.processor.reader.InventoryReader;
21+
import org.metaeffekt.core.inventory.processor.report.configuration.ReportConfigurationParameters;
22+
23+
import java.io.File;
24+
import java.io.IOException;
25+
26+
public class AnnexResourceProcessorTest {
27+
28+
@Test
29+
public void testLicenseResourceProcessor () throws IOException {
30+
31+
InventoryReader reader = new InventoryReader();
32+
String resourceDir = "src/test/resources/license-resource-processor/reference-inventory";
33+
Inventory inventory = reader.readInventory(new File(resourceDir, "inventory/artifact-inventory-01.xls"));
34+
Inventory referenceInventory = reader.readInventory(new File(resourceDir, "inventory/artifact-inventory-01.xls"));
35+
ReportConfigurationParameters configParams = ReportConfigurationParameters.builder().build();
36+
File referenceInventoryDir = new File (resourceDir);
37+
String referenceComponentPath = "components";
38+
String referenceLicensePath = "licenses";
39+
File targetComponentDir = new File("target/license-resource-processor/inventory/components");
40+
File targetLicenseDir = new File("target/license-resource-processor/inventory/licenses");
41+
42+
AnnexResourceProcessor processor = new AnnexResourceProcessor(inventory, referenceInventory, configParams, referenceInventoryDir, referenceComponentPath, referenceLicensePath, targetComponentDir, targetLicenseDir);
43+
processor.execute();
44+
}
45+
}
46+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Sample text of A License associated with Component Alpha.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Sample text of A License Notice associated with Component Alpha.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Sample text of dual license.
2+
3+
AlphaBeta is licensed either under license A or license B.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Sample text of B License associated with Component Beta.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Sample text of B License Notice associated with Component Beta.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Sample text of D License associated with Component Delta.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Sample text of G License associated with Component Gamma.

0 commit comments

Comments
 (0)