This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-analyser.git
The following commit(s) were added to refs/heads/master by this push:
new 301fcfa SLING-13427 : Export / import analysers should take
bootdelegation into account
301fcfa is described below
commit 301fcfae7ff46bc0b39c26b91de4bd364e42bb46
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Fri Nov 13 08:50:38 2026 +0100
SLING-13427 : Export / import analysers should take bootdelegation into
account
---
.../task/impl/CheckBundleExportsImports.java | 23 +++++++
.../task/impl/CheckBundleExportsImportsTest.java | 72 ++++++++++++++++++++++
2 files changed, 95 insertions(+)
diff --git
a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImports.java
b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImports.java
index efe13ff..fe6c7fa 100644
---
a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImports.java
+++
b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImports.java
@@ -30,6 +30,7 @@ import org.apache.sling.feature.analyser.task.AnalyserTask;
import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
import org.apache.sling.feature.scanner.BundleDescriptor;
import org.apache.sling.feature.scanner.PackageInfo;
+import org.osgi.framework.Constants;
public class CheckBundleExportsImports implements AnalyserTask {
@@ -62,6 +63,10 @@ public class CheckBundleExportsImports implements
AnalyserTask {
public void execute(final AnalyserTaskContext ctx) throws IOException {
// basic checks
final Map<BundleDescriptor, Report> reports = new HashMap<>();
+ final String[] bootDelegation = ctx.getFeature()
+ .getFrameworkProperties()
+ .getOrDefault(Constants.FRAMEWORK_BOOTDELEGATION, "")
+ .split(",");
final SortedMap<Integer, List<BundleDescriptor>> bundlesMap = new
TreeMap<>();
for (final BundleDescriptor bi :
ctx.getFeatureDescriptor().getBundleDescriptors()) {
@@ -88,6 +93,9 @@ public class CheckBundleExportsImports implements
AnalyserTask {
for (final PackageInfo pck : info.getImportedPackages()) {
final List<BundleDescriptor> candidates =
getCandidates(exportingBundles, pck);
if (candidates.isEmpty()) {
+ if (isBootDelegated(bootDelegation, pck.getName())) {
+ continue;
+ }
if (pck.isOptional()) {
getReport(reports,
info).missingExportsForOptional.add(pck);
} else {
@@ -101,6 +109,9 @@ public class CheckBundleExportsImports implements
AnalyserTask {
}
}
if (matchingCandidates.isEmpty()) {
+ if (isBootDelegated(bootDelegation,
pck.getName())) {
+ continue;
+ }
if (pck.isOptional()) {
getReport(reports, info)
.missingExportsForOptional
@@ -145,6 +156,18 @@ public class CheckBundleExportsImports implements
AnalyserTask {
}
}
+ private boolean isBootDelegated(final String[] bootDelegation, final
String packageName) {
+ for (final String entry : bootDelegation) {
+ final String pattern = entry.trim();
+ if ("*".equals(pattern)
+ || packageName.equals(pattern)
+ || (pattern.endsWith(".*") &&
packageName.startsWith(pattern.substring(0, pattern.length() - 1)))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private String getPackageInfo(final List<PackageInfo> pcks, final boolean
includeVersion) {
if (pcks.size() == 1) {
final StringBuilder sb = new StringBuilder("package ");
diff --git
a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImportsTest.java
b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImportsTest.java
index 7a068d5..72f7a23 100644
---
a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImportsTest.java
+++
b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckBundleExportsImportsTest.java
@@ -21,6 +21,7 @@ package org.apache.sling.feature.analyser.task.impl;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
+import java.util.jar.Manifest;
import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.ArtifactId;
@@ -36,6 +37,7 @@ import
org.apache.sling.feature.scanner.impl.FeatureDescriptorImpl;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
+import org.osgi.framework.Constants;
import static org.junit.Assert.assertEquals;
@@ -157,6 +159,76 @@ public class CheckBundleExportsImportsTest {
Mockito.verify(ctx,
Mockito.never()).reportWarning(Mockito.anyString());
}
+ @Test
+ public void testBootDelegationMatches() throws IOException {
+ for (String bootDelegation : new String[] {
+ "org.foo.bar", "org.foo.*", "org.*", "*", " org.other ,
org.foo.bar ", "org.other, org.foo.*"
+ }) {
+ AnalyserTaskContext ctx = bootDelegationContext(bootDelegation,
null);
+
+ new CheckBundleExportsImports().execute(ctx);
+
+ Mockito.verify(ctx,
Mockito.never()).reportArtifactError(Mockito.any(), Mockito.anyString());
+ Mockito.verify(ctx,
Mockito.never()).reportError(Mockito.anyString());
+ }
+ }
+
+ @Test
+ public void testBootDelegationDoesNotMatch() throws IOException {
+ for (String bootDelegation : new String[] {
+ null, "", " ", "org.other", "org.foo", "org.foo.bar.*",
"org.fo.*", "org.foo.b*", "org.foo.bar.other"
+ }) {
+ AnalyserTaskContext ctx = bootDelegationContext(bootDelegation,
null);
+
+ new CheckBundleExportsImports().execute(ctx);
+
+ Mockito.verify(ctx).reportArtifactError(Mockito.any(),
Mockito.contains("org.foo.bar"));
+ Mockito.verify(ctx).reportError(Mockito.contains("marked as
'complete'"));
+ }
+ }
+
+ @Test
+ public void testBootDelegationWithExistingExport() throws IOException {
+ for (String exportVersion : new String[] {"1.0", "2.0"}) {
+ for (String bootDelegation : new String[] {"org.foo.*",
"org.other"}) {
+ AnalyserTaskContext ctx =
bootDelegationContext(bootDelegation, exportVersion);
+
+ new CheckBundleExportsImports().execute(ctx);
+
+ if ("1.0".equals(exportVersion) &&
"org.other".equals(bootDelegation)) {
+ Mockito.verify(ctx).reportArtifactError(Mockito.any(),
Mockito.contains("required version range"));
+ Mockito.verify(ctx).reportError(Mockito.contains("marked
as 'complete'"));
+ } else {
+ Mockito.verify(ctx,
Mockito.never()).reportArtifactError(Mockito.any(), Mockito.anyString());
+ Mockito.verify(ctx,
Mockito.never()).reportError(Mockito.anyString());
+ }
+ }
+ }
+ }
+
+ private AnalyserTaskContext bootDelegationContext(String bootDelegation,
String exportVersion) throws IOException {
+ Feature f = new Feature(ArtifactId.fromMvnId("f:f:1"));
+ f.setComplete(true);
+ if (bootDelegation != null) {
+ f.getFrameworkProperties().put(Constants.FRAMEWORK_BOOTDELEGATION,
bootDelegation);
+ }
+ FeatureDescriptor fd = new FeatureDescriptorImpl(f);
+ Manifest manifest = new Manifest();
+
manifest.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+ manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME,
"test-bundle");
+ manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION,
"1.0.0");
+ manifest.getMainAttributes().putValue(Constants.IMPORT_PACKAGE,
"org.foo.bar;version=\"[2.0,3.0)\"");
+ if (exportVersion != null) {
+ manifest.getMainAttributes().putValue(Constants.EXPORT_PACKAGE,
"org.foo.bar;version=" + exportVersion);
+ }
+ fd.getBundleDescriptors()
+ .add(new BundleDescriptorImpl(new
Artifact(ArtifactId.fromMvnId("g:b:1")), null, manifest));
+ AnalyserTaskContext ctx = Mockito.mock(AnalyserTaskContext.class);
+ Mockito.when(ctx.getFeature()).thenReturn(f);
+ Mockito.when(ctx.getFeatureDescriptor()).thenReturn(fd);
+ return ctx;
+ }
+
private void fdAddBundle(FeatureDescriptor fd, String id, String file)
throws IOException {
BundleDescriptor bd1 = new BundleDescriptorImpl(
new Artifact(ArtifactId.fromMvnId(id)),