diff --git a/cbor/src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper b/cbor/src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper index bce60816d..d7a5f6495 100644 --- a/cbor/src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper +++ b/cbor/src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper @@ -1 +1 @@ -tools.jackson.dataformat.cbor.databind.CBORMapper +tools.jackson.dataformat.cbor.CBORMapper diff --git a/cbor/src/test/java/tools/jackson/dataformat/cbor/ServiceLoader700Test.java b/cbor/src/test/java/tools/jackson/dataformat/cbor/ServiceLoader700Test.java new file mode 100644 index 000000000..942332259 --- /dev/null +++ b/cbor/src/test/java/tools/jackson/dataformat/cbor/ServiceLoader700Test.java @@ -0,0 +1,50 @@ +package tools.jackson.dataformat.cbor; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; + +import static org.junit.jupiter.api.Assertions.*; + +// [dataformats-binary#700]: SPI file referenced wrong class name +// (`...cbor.databind.CBORMapper` instead of `...cbor.CBORMapper`), +// causing `ServiceConfigurationError` from `ServiceLoader` for classpath +// (non-modular) usage. +// +// NOTE: the SPI file only governs classpath usage; modular usage is covered by +// the `provides` directive in `module-info.java`. Since the test harness runs on +// the module path (where the module's own resources are not reachable), this test +// validates the source SPI file content directly instead of via `ServiceLoader`. +public class ServiceLoader700Test extends CBORTestBase +{ + private final static File SERVICE_FILE = new File( + "src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper"); + + @Test + public void testServiceFileClassNamesResolve() throws Exception + { + assertTrue(SERVICE_FILE.exists(), "Missing SPI file: " + SERVICE_FILE.getAbsolutePath()); + boolean foundCBORMapper = false; + List lines = Files.readAllLines(SERVICE_FILE.toPath(), StandardCharsets.UTF_8); + for (String line : lines) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + // Class named in SPI file must actually exist and be an `ObjectMapper`... + Class cls = Class.forName(line); + assertTrue(ObjectMapper.class.isAssignableFrom(cls), + "Class `" + line + "` is not an `ObjectMapper` subtype"); + if (cls == CBORMapper.class) { + foundCBORMapper = true; + } + } + assertTrue(foundCBORMapper, + "SPI file should list `" + CBORMapper.class.getName() + "`"); + } +} diff --git a/release-notes/CREDITS b/release-notes/CREDITS index 744a983a1..2acaa1f0d 100644 --- a/release-notes/CREDITS +++ b/release-notes/CREDITS @@ -44,3 +44,9 @@ Shanchao Li (@tonghuaroot) * Reported #693: (avro) Incomplete number length validation in Avro decoder (for `BigDecimal`) (3.1.4) + +Juan Farré (@jafarre-bi) + +* Reported #700: (cbor, smile) `META-INF/services/tools.jackson.databind.ObjectMapper` + references wrong class name (`...databind.CBORMapper` / `...databind.SmileMapper`) + (3.1.5) diff --git a/release-notes/VERSION b/release-notes/VERSION index 3a3a0adac..7eb77d2f8 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -18,6 +18,12 @@ implementations) No changes since 3.1 +3.1.5 (not yet released) + +#700: (cbor, smile) `META-INF/services/tools.jackson.databind.ObjectMapper` + references wrong class name (`...databind.CBORMapper` / `...databind.SmileMapper`) + (reported by Juan F) + 3.1.4 (29-May-2026) #691: (cbor) Add parameterized tests covering all ASCII-optimization exit paths diff --git a/smile/src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper b/smile/src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper index 95a4fdeef..3a02fc3b4 100644 --- a/smile/src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper +++ b/smile/src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper @@ -1 +1 @@ -tools.jackson.dataformat.smile.databind.SmileMapper +tools.jackson.dataformat.smile.SmileMapper diff --git a/smile/src/test/java/tools/jackson/dataformat/smile/ServiceLoader700Test.java b/smile/src/test/java/tools/jackson/dataformat/smile/ServiceLoader700Test.java new file mode 100644 index 000000000..b9c33270f --- /dev/null +++ b/smile/src/test/java/tools/jackson/dataformat/smile/ServiceLoader700Test.java @@ -0,0 +1,50 @@ +package tools.jackson.dataformat.smile; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; + +import static org.junit.jupiter.api.Assertions.*; + +// [dataformats-binary#700]: SPI file referenced wrong class name +// (`...smile.databind.SmileMapper` instead of `...smile.SmileMapper`), +// causing `ServiceConfigurationError` from `ServiceLoader` for classpath +// (non-modular) usage. +// +// NOTE: the SPI file only governs classpath usage; modular usage is covered by +// the `provides` directive in `module-info.java`. Since the test harness runs on +// the module path (where the module's own resources are not reachable), this test +// validates the source SPI file content directly instead of via `ServiceLoader`. +public class ServiceLoader700Test extends BaseTestForSmile +{ + private final static File SERVICE_FILE = new File( + "src/main/resources/META-INF/services/tools.jackson.databind.ObjectMapper"); + + @Test + public void testServiceFileClassNamesResolve() throws Exception + { + assertTrue(SERVICE_FILE.exists(), "Missing SPI file: " + SERVICE_FILE.getAbsolutePath()); + boolean foundSmileMapper = false; + List lines = Files.readAllLines(SERVICE_FILE.toPath(), StandardCharsets.UTF_8); + for (String line : lines) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + // Class named in SPI file must actually exist and be an `ObjectMapper`... + Class cls = Class.forName(line); + assertTrue(ObjectMapper.class.isAssignableFrom(cls), + "Class `" + line + "` is not an `ObjectMapper` subtype"); + if (cls == SmileMapper.class) { + foundSmileMapper = true; + } + } + assertTrue(foundSmileMapper, + "SPI file should list `" + SmileMapper.class.getName() + "`"); + } +}