From a6002a674fa7583dccd525b4206c69196d8f4787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Sat, 29 Aug 2026 22:41:25 +0200 Subject: [PATCH 1/3] Fix FilterImpl.WrapperCapability, broken by a generics cleanup Two defects were introduced in 466eb93f1c ("[fw] reduce warning related to types Classes"), both in org.apache.felix.framework.FilterImpl.WrapperCapability. They were found by the OSGi Core R8 TCK, which reported 33 errors across BundleContextFilterTests and DivTests. 1. Filter.matches(Map) throws for any non-empty map. The constructor lost its assignment into a stray empty if block: m_map = Collections.emptyMap(); if(map != null ) { } m_map.putAll(map); m_map therefore refers to an immutable empty map and putAll throws UnsupportedOperationException, or NullPointerException when map is null. It previously read: m_map = (map == null) ? Collections.EMPTY_MAP : map; which is restored. Filter.matches(Map) has been unusable since April 2025. 2. WrapperCapability(ServiceReference) requires an OSGi Core 1.10 method. It was rewritten to read properties via new DictionaryToMap(sr.getProperties(), false). ServiceReference.getProperties() was only added in Core 1.10 and is not implemented by every ServiceReference; the TCK's own mock throws UnsupportedOperationException for it. Restored to the getPropertyKeys()/getProperty() loop, which every implementation supports. Adds regression tests for both. They exercise org.apache.felix.framework.FilterImpl directly, since FrameworkUtil.createFilter returns the unrelated org.osgi.framework.FilterImpl. Verified that both fail with UnsupportedOperationException against the current code and pass with the fix. Co-Authored-By: Claude Opus 4.8 --- .../apache/felix/framework/FilterImpl.java | 15 +++++--- .../apache/felix/framework/FilterTest.java | 34 +++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/framework/src/main/java/org/apache/felix/framework/FilterImpl.java b/framework/src/main/java/org/apache/felix/framework/FilterImpl.java index 2dddad0e25..5ff4982215 100644 --- a/framework/src/main/java/org/apache/felix/framework/FilterImpl.java +++ b/framework/src/main/java/org/apache/felix/framework/FilterImpl.java @@ -104,13 +104,11 @@ static class WrapperCapability extends BundleCapabilityImpl { private final Map m_map; + @SuppressWarnings("unchecked") public WrapperCapability(Map map) { super(null, null, Collections.emptyMap(), Collections.emptyMap()); - m_map = Collections.emptyMap(); - if(map != null ) { - } - m_map.putAll(map); + m_map = (map == null) ? Collections.emptyMap() : (Map) map; } public WrapperCapability(Dictionary dict, boolean caseSensitive) @@ -122,7 +120,14 @@ public WrapperCapability(Dictionary dict, boolean caseSensitive) public WrapperCapability(ServiceReference sr) { super(null, null, Collections.emptyMap(), Collections.emptyMap()); - m_map = new DictionaryToMap(sr.getProperties(), false); + // Read the properties one by one rather than via getProperties(): that + // method was only added in OSGi Core 1.10 and is not implemented by every + // ServiceReference, whereas getPropertyKeys()/getProperty() always are. + m_map = new StringMap(); + for (String key : sr.getPropertyKeys()) + { + m_map.put(key, sr.getProperty(key)); + } } @Override diff --git a/framework/src/test/java/org/apache/felix/framework/FilterTest.java b/framework/src/test/java/org/apache/felix/framework/FilterTest.java index 6397ce85b9..e1746267e1 100644 --- a/framework/src/test/java/org/apache/felix/framework/FilterTest.java +++ b/framework/src/test/java/org/apache/felix/framework/FilterTest.java @@ -23,10 +23,12 @@ import java.util.Collection; import java.util.Collections; import java.util.Dictionary; +import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; @@ -104,4 +106,36 @@ private static Dictionary createTestDict(Object o) return dictionary; } + /** + * Filter.matches(Map) used to throw UnsupportedOperationException for any + * non-empty map, because WrapperCapability assigned an immutable empty map and + * then called putAll on it. See FELIX-6759 discussion; regression from + * 466eb93f1c. + */ + @Test + void matchesNonEmptyMap() throws InvalidSyntaxException + { + Filter filter = new FilterImpl("(one=one-value)"); + + Map map = new HashMap<>(); + map.put("one", "one-value"); + + assertThat(filter.matches(map)).isTrue(); + assertThat(filter.matches(Collections.singletonMap("one", "other-value"))).isFalse(); + } + + /** + * The same constructor threw NullPointerException for a null map, where it used + * to fall back to an empty one. + */ + @Test + void matchesEmptyAndUnmodifiableMap() throws InvalidSyntaxException + { + Filter filter = new FilterImpl("(one=one-value)"); + + assertThat(filter.matches(Collections.emptyMap())).isFalse(); + assertThat(filter.matches( + Collections.unmodifiableMap(Collections.singletonMap("one", (Object) "one-value")))).isTrue(); + } + } From 8a0efb9e8b82c9150320b5caa39b5a99443d6127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Sat, 29 Aug 2026 22:45:38 +0200 Subject: [PATCH 2/3] Fix the TCK dependency ranges so the framework TCK can run The OSGi Core TCK currently cannot resolve on any JDK, so framework.tck fails before executing a single test. That is what hid the FilterImpl defects fixed in this branch, and it also means CI cannot demonstrate the fix without repairing it. Two ranges were left behind when assertj-core was bumped from 3.27.3 to 3.27.7 in #478: - tck.bndrun still required assertj-core [3.27.3,3.27.4), so the bndrun could not be resolved at all: "assertj-core;version=[3.27.3,3.27.4) Not found in [... assertj-core;version=3.27.7 ...]". - assertj-core 3.27.7 imports net.bytebuddy [1.18.0,2.0.0) but byte-buddy was pinned at 1.17.5, so assertj-core then failed to start with an unresolved osgi.wiring.package requirement. Also removes a duplicate junit-platform-launcher dependency, which Maven reports as a malformed model and warns it may reject in future. Co-Authored-By: Claude Opus 4.8 --- framework.tck/pom.xml | 8 +------- framework.tck/tck.bndrun | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/framework.tck/pom.xml b/framework.tck/pom.xml index 35cd65a10d..b0369c0279 100644 --- a/framework.tck/pom.xml +++ b/framework.tck/pom.xml @@ -105,7 +105,7 @@ net.bytebuddy byte-buddy - 1.17.5 + 1.18.12 test @@ -120,12 +120,6 @@ 4.13.2_1 test - - org.junit.platform - junit-platform-launcher - 1.12.1 - test - org.junit.platform junit-platform-engine diff --git a/framework.tck/tck.bndrun b/framework.tck/tck.bndrun index 541ec5ddb6..72a7a22274 100644 --- a/framework.tck/tck.bndrun +++ b/framework.tck/tck.bndrun @@ -30,7 +30,7 @@ junit-platform-engine;version='[1.12.1,1.12.2)',\ org.opentest4j;version='[1.3.0,1.3.1)',\ junit-platform-launcher;version='[1.12.1,1.12.2)',\ - assertj-core;version='[3.27.3,3.27.4)',\ + assertj-core;version='[3.27.7,3.27.8)',\ biz.aQute.junit;version='[6.4.1,6.4.2)',\ junit-vintage-engine;version='[5.7.1,5.7.2)',\ - net.bytebuddy.byte-buddy;version='[1.17.5,1.17.6)' \ No newline at end of file + net.bytebuddy.byte-buddy;version='[1.18.12,1.18.13)' \ No newline at end of file From e21afaaacd93cc933c9ffdaa2bc8d42ac272f7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20R=C3=BCtter?= Date: Sun, 30 Aug 2026 00:00:05 +0200 Subject: [PATCH 3/3] Install the framework in CI so the TCK tests the local build The framework step ran clean verify, which does not install. The TCK is a separate Maven invocation, so it resolves org.apache.felix.framework from the repository rather than from the build that just ran, and org.apache.felix.framework 7.1.0-SNAPSHOT exists in apache.snapshots. Resolution therefore succeeded against the published snapshot and the TCK never exercised the code under test. That matters for this pull request in particular: the FilterImpl defects it fixes are exactly what the TCK reports, so a green TCK run proved nothing while the framework was being resolved from elsewhere. Running clean install makes the TCK test the framework this build produced. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/maven-ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven-ci.yml b/.github/workflows/maven-ci.yml index c9599684d1..9218fb9923 100644 --- a/.github/workflows/maven-ci.yml +++ b/.github/workflows/maven-ci.yml @@ -86,7 +86,11 @@ jobs: run: mvn -B -V -Dstyle.color=always --file webconsole/pom.xml clean install verify - name: Felix Framework if: steps.changes.outputs.framework == 'true' - run: mvn -B -V -Dstyle.color=always --file framework/pom.xml clean verify + # install, not verify: the TCK below is a separate Maven invocation and resolves + # the framework from the repository, so without installing it silently tests + # whatever org.apache.felix.framework is published rather than the build under + # test. + run: mvn -B -V -Dstyle.color=always --file framework/pom.xml clean install - name: OSGi-TCK Framework if: steps.changes.outputs.framework == 'true' run: mvn -B -V -Dstyle.color=always --file framework.tck/pom.xml clean verify