T[] nullIfEmpty(T[] arg) {
+ if (arg == null || arg.length == 0) {
+ return null;
+ }
+ return arg;
+ }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/JakartaRSFeature.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/JakartaRSFeature.java
new file mode 100644
index 00000000000..1257c794c08
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/JakartaRSFeature.java
@@ -0,0 +1,128 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg;
+
+import tools.jackson.databind.cfg.ConfigFeature;
+
+/**
+ * Enumeration that defines simple on/off features that can be
+ * used on all Jackson Jakarta-RS providers, regardless of
+ * underlying data format.
+ */
+public enum JakartaRSFeature implements ConfigFeature
+{
+ /*
+ /**********************************************************************
+ /* Input handling
+ /**********************************************************************
+ */
+
+ /**
+ * Feature to define whether empty input is considered legal or not.
+ * If set to true, empty content is allowed and will be read as Java 'null': if false,
+ * an {@link java.io.IOException} will be thrown.
+ *
+ * NOTE: in case of Jakarta-RS 2.0, specific exception will be {@code jakarta.ws.rs.core.NoContentException},
+ */
+ ALLOW_EMPTY_INPUT(true),
+
+ /**
+ * For HTTP keep-alive or multipart content to work correctly, Jackson must read the entire HTTP input
+ * stream up until reading EOF (-1).
+ * Issue #108
+ * If set to true, always consume all input content. This has a side-effect of failing on trailing content.
+ *
+ * Feature is enabled by default.
+ * Note that this means that behavior in earlier versions
+ * (2.14 and before) differs from 2.15 and later.
+ *
+ * @since 2.15
+ */
+ READ_FULL_STREAM(true),
+
+ /*
+ /**********************************************************************
+ /* HTTP headers
+ /**********************************************************************
+ */
+
+ /**
+ * Feature that can be enabled to make provider automatically
+ * add "nosniff" (see
+ * this entry
+ * for details
+ *
+ * Feature is disabled by default.
+ */
+ ADD_NO_SNIFF_HEADER(false),
+
+ /*
+ /**********************************************************************
+ /* Caching, related
+ /**********************************************************************
+ */
+
+ /**
+ * Feature that may be enabled to force dynamic lookup of ObjectMapper
+ * via Jakarta-RS Provider interface, regardless of whether MapperConfigurator
+ * has explicitly configured mapper or not; if disabled, static configuration will
+ * take precedence.
+ * Note that if this feature is enabled, it typically makes sense to also disable
+ * {@link JakartaRSFeature#CACHE_ENDPOINT_READERS} and {@link JakartaRSFeature#CACHE_ENDPOINT_WRITERS}
+ * since caching would prevent lookups.
+ *
+ * Feature is disabled by default.
+ */
+ DYNAMIC_OBJECT_MAPPER_LOOKUP(false),
+
+ /**
+ * Feature that determines whether provider will cache endpoint
+ * definitions for reading or not (including caching of actual ObjectReader to use).
+ * Feature may be disabled if reconfiguration or alternate instance of ObjectMapper is needed.
+ *
+ * Note that disabling of the feature may add significant amount of overhead for processing.
+ *
+ * Feature is enabled by default.
+ */
+ CACHE_ENDPOINT_READERS(true),
+
+ /**
+ * Feature that determines whether provider will cache endpoint
+ * definitions for writing or not (including caching of actual ObjectWriter to use).
+ * Feature may be disabled if reconfiguration or alternate instance of ObjectMapper is needed.
+ *
+ * Note that disabling of the feature may add significant amount of overhead for processing.
+ *
+ * Feature is enabled by default.
+ */
+ CACHE_ENDPOINT_WRITERS(true),
+
+ /*
+ /**********************************************************************
+ /* Other
+ /**********************************************************************
+ */
+
+ ;
+
+ private final boolean _defaultState;
+
+ private JakartaRSFeature(boolean defaultState) {
+ _defaultState = defaultState;
+ }
+
+ public static int collectDefaults() {
+ int flags = 0;
+ for (JakartaRSFeature f : values()) {
+ if (f.enabledByDefault()) { flags |= f.getMask(); }
+ }
+ return flags;
+ }
+
+ @Override
+ public boolean enabledByDefault() { return _defaultState; }
+
+ @Override
+ public int getMask() { return (1 << ordinal()); }
+
+ @Override
+ public boolean enabledIn(int flags) { return (flags & getMask()) != 0; }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/MapperConfiguratorBase.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/MapperConfiguratorBase.java
new file mode 100644
index 00000000000..3048116f4d5
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/MapperConfiguratorBase.java
@@ -0,0 +1,222 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg;
+
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
+
+import tools.jackson.databind.AnnotationIntrospector;
+import tools.jackson.databind.DeserializationFeature;
+import tools.jackson.databind.MapperFeature;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.SerializationFeature;
+import tools.jackson.databind.cfg.MapperBuilder;
+
+/**
+ * Helper class used to encapsulate details of configuring an
+ * {@link ObjectMapper} instance to be used for data binding, as
+ * well as accessing it.
+ */
+public abstract class MapperConfiguratorBase,
+ MAPPER extends ObjectMapper
+>
+{
+ private final ReentrantLock _lock = new ReentrantLock();
+
+ /*
+ /**********************************************************************
+ /* Configuration, simple features
+ /**********************************************************************
+ */
+
+ /**
+ * {@link DeserializationFeature}s to explicitly enable or disable
+ */
+ protected EnumMap _mapperFeatures;
+
+ /**
+ * {@link DeserializationFeature}s to explicitly enable or disable
+ */
+ protected EnumMap _deserFeatures;
+
+ /**
+ * {@link SerializationFeature}s to explicitly enable or disable
+ */
+ protected EnumMap _serFeatures;
+
+ /*
+ /**********************************************************************
+ /* Configuration, other
+ /**********************************************************************
+ */
+
+ /**
+ * {@code AnnotationIntrospector} to use as an override over default
+ * {@code JacksonAnnotationIntrospector}, if any.
+ *
+ * @since 3.0
+ */
+ protected AnnotationIntrospector _instropectorOverride;
+
+ /*
+ /**********************************************************************
+ /* Lazily constructed Mapper instance(s)
+ /**********************************************************************
+ */
+
+ /**
+ * Mapper provider was constructed with if any, or that was constructed
+ * due to a call to explicitly configure mapper.
+ * If defined (explicitly or implicitly) it will be used, instead
+ * of using provider-based lookup.
+ */
+ protected MAPPER _mapper;
+
+ /**
+ * If no mapper was specified when constructed, and no configuration
+ * calls are made, a default mapper is constructed. The difference
+ * between default mapper and regular one is that default mapper
+ * is only used if no mapper is found via provider lookup.
+ */
+ protected MAPPER _defaultMapper;
+
+ /*
+ /**********************************************************************
+ /* Life-cycle
+ /**********************************************************************
+ */
+
+ public MapperConfiguratorBase(MAPPER mapper,
+ AnnotationIntrospector instropectorOverride)
+ {
+ _mapper = mapper;
+ _instropectorOverride = instropectorOverride;
+ }
+
+ public MAPPER getDefaultMapper() {
+ if (_defaultMapper == null) {
+ _lock.lock();
+ try {
+ if (_defaultMapper == null) {
+ _defaultMapper = _mapperWithConfiguration(mapperBuilder());
+ }
+ } finally {
+ _lock.unlock();
+ } }
+ return _defaultMapper;
+ }
+
+ /**
+ * Helper method that will ensure that there is a configurable non-default
+ * mapper (constructing an instance if one didn't yet exit), and return
+ * that mapper.
+ */
+ protected MAPPER mapper()
+ {
+ if (_mapper == null) {
+ _lock.lock();
+ try {
+ if (_mapper == null) {
+ _mapper = _mapperWithConfiguration(mapperBuilder());
+ }
+ } finally {
+ _lock.unlock();
+ }
+ }
+ return _mapper;
+ }
+
+ /*
+ /**********************************************************************
+ /* Abstract methods to implement
+ /**********************************************************************
+ */
+
+ /**
+ * @since 3.0
+ */
+ protected abstract MapperBuilder,?> mapperBuilder();
+
+ /*
+ /**********************************************************************
+ /* Configuration methods
+ /**********************************************************************
+ */
+
+ /**
+ * Method that locates, configures and returns {@link ObjectMapper} to use
+ */
+ public MAPPER getConfiguredMapper() {
+ // important: should NOT call mapper(); needs to return null
+ // if no instance has been passed or constructed
+ return _mapper;
+ }
+
+ public final void setMapper(MAPPER m) {
+ _mapper = m;
+ }
+
+ public final void setAnnotationIntrospector(AnnotationIntrospector aiOverride) {
+ _instropectorOverride = aiOverride;
+ }
+
+ public final void configure(DeserializationFeature f, boolean state) {
+ if (_deserFeatures == null) {
+ _deserFeatures = new EnumMap<>(DeserializationFeature.class);
+ }
+ _deserFeatures.put(f, state);
+ }
+
+ public final void configure(SerializationFeature f, boolean state) {
+ if (_serFeatures == null) {
+ _serFeatures = new EnumMap<>(SerializationFeature.class);
+ }
+ _serFeatures.put(f, state);
+ }
+
+ /*
+ /**********************************************************************
+ /* Helper methods for sub-classes
+ /**********************************************************************
+ */
+
+ /**
+ * Helper method that will configure given builder using configured overrides.
+ */
+ @SuppressWarnings("unchecked")
+ protected MAPPER _mapperWithConfiguration(MapperBuilder,?> mapperBuilder)
+ {
+ return (MAPPER) _builderWithConfiguration(mapperBuilder)
+ .build();
+ }
+
+ /**
+ * Overridable helper method that applies all configuration on given builder.
+ */
+ protected MapperBuilder,?> _builderWithConfiguration(MapperBuilder,?> mapperBuilder)
+ {
+ // First, AnnotationIntrospector settings
+ if (_instropectorOverride != null) {
+ mapperBuilder = mapperBuilder.annotationIntrospector(_instropectorOverride);
+ }
+
+ // Features?
+ if (_mapperFeatures != null) {
+ for (Map.Entry entry : _mapperFeatures.entrySet()) {
+ mapperBuilder = mapperBuilder.configure(entry.getKey(), entry.getValue());
+ }
+ }
+ if (_serFeatures != null) {
+ for (Map.Entry entry : _serFeatures.entrySet()) {
+ mapperBuilder = mapperBuilder.configure(entry.getKey(), entry.getValue());
+ }
+ }
+ if (_deserFeatures != null) {
+ for (Map.Entry entry : _deserFeatures.entrySet()) {
+ mapperBuilder = mapperBuilder.configure(entry.getKey(), entry.getValue());
+ }
+ }
+
+ // anything else?
+ return mapperBuilder;
+ }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectReaderInjector.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectReaderInjector.java
new file mode 100644
index 00000000000..0354c925f81
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectReaderInjector.java
@@ -0,0 +1,43 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import tools.jackson.databind.ObjectReader;
+
+/**
+ * This class allows registering a
+ * modifier ({@link ObjectReaderModifier}) that can be used to
+ * reconfigure {@link ObjectReader}
+ * that Jakarta-RS Resource will use for reading input into Java Objects.
+ * Usually this class is accessed from a Servlet or Jakarta-RS filter
+ * before execution reaches resource.
+ */
+public class ObjectReaderInjector
+{
+ protected static final ThreadLocal _threadLocal = new ThreadLocal();
+
+ /**
+ * Simple marker used to optimize out {@link ThreadLocal} access in cases
+ * where this feature is not being used
+ */
+ protected static final AtomicBoolean _hasBeenSet = new AtomicBoolean(false);
+
+ private ObjectReaderInjector() { }
+
+ public static void set(ObjectReaderModifier mod) {
+ _hasBeenSet.set(true);
+ _threadLocal.set(mod);
+ }
+
+ public static ObjectReaderModifier get() {
+ return _hasBeenSet.get() ? _threadLocal.get() : null;
+ }
+
+ public static ObjectReaderModifier getAndClear() {
+ ObjectReaderModifier mod = get();
+ if (mod != null) {
+ _threadLocal.remove();
+ }
+ return mod;
+ }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectReaderModifier.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectReaderModifier.java
new file mode 100644
index 00000000000..44d16abd983
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectReaderModifier.java
@@ -0,0 +1,26 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg;
+
+import tools.jackson.core.JacksonException;
+import tools.jackson.core.JsonParser;
+import tools.jackson.databind.JavaType;
+import tools.jackson.databind.ObjectReader;
+
+import jakarta.ws.rs.core.MultivaluedMap;
+
+public abstract class ObjectReaderModifier
+{
+ /**
+ * Method called to let modifier make any changes it wants to to objects
+ * used for reading request objects for specified endpoint.
+ *
+ * @param endpoint End point for which reader is used
+ * @param httpHeaders HTTP headers sent with request (read-only)
+ * @param resultType Type that input is to be bound to
+ * @param r ObjectReader as constructed for endpoint, type to handle
+ * @param p Parser to use for reading content
+ */
+ public abstract ObjectReader modify(EndpointConfigBase> endpoint,
+ MultivaluedMap httpHeaders,
+ JavaType resultType, ObjectReader r, JsonParser p)
+ throws JacksonException;
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectWriterInjector.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectWriterInjector.java
new file mode 100644
index 00000000000..2965d7b6208
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectWriterInjector.java
@@ -0,0 +1,40 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import tools.jackson.databind.ObjectWriter;
+
+/**
+ * This class allows "overriding" of {@link ObjectWriter}
+ * that Jakarta-RS Resource will use; usually this is done from a Servlet
+ * or Jakarta-RS filter before execution reaches resource.
+ */
+public class ObjectWriterInjector
+{
+ protected static final ThreadLocal _threadLocal = new ThreadLocal();
+
+ /**
+ * Simple marker used to optimize out {@link ThreadLocal} access in cases
+ * where this feature is not being used
+ */
+ protected static final AtomicBoolean _hasBeenSet = new AtomicBoolean(false);
+
+ private ObjectWriterInjector() { }
+
+ public static void set(ObjectWriterModifier mod) {
+ _hasBeenSet.set(true);
+ _threadLocal.set(mod);
+ }
+
+ public static ObjectWriterModifier get() {
+ return _hasBeenSet.get() ? _threadLocal.get() : null;
+ }
+
+ public static ObjectWriterModifier getAndClear() {
+ ObjectWriterModifier mod = get();
+ if (mod != null) {
+ _threadLocal.remove();
+ }
+ return mod;
+ }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectWriterModifier.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectWriterModifier.java
new file mode 100644
index 00000000000..5cd29f9a435
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/cfg/ObjectWriterModifier.java
@@ -0,0 +1,20 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg;
+
+import tools.jackson.core.JacksonException;
+import tools.jackson.databind.ObjectWriter;
+
+import jakarta.ws.rs.core.MultivaluedMap;
+
+public abstract class ObjectWriterModifier
+{
+ /**
+ * Method called to let modifier make any changes it wants to to objects
+ * used for writing response for specified endpoint.
+ *
+ * @param responseHeaders HTTP headers being returned with response (mutable)
+ */
+ public abstract ObjectWriter modify(EndpointConfigBase> endpoint,
+ MultivaluedMap responseHeaders,
+ Object valueToWrite, ObjectWriter w)
+ throws JacksonException;
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JacksonJsonProvider.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JacksonJsonProvider.java
new file mode 100644
index 00000000000..5a06039e4e5
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JacksonJsonProvider.java
@@ -0,0 +1,234 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json;
+
+import java.lang.annotation.Annotation;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.ext.ContextResolver;
+import jakarta.ws.rs.ext.MessageBodyReader;
+import jakarta.ws.rs.ext.MessageBodyWriter;
+import jakarta.ws.rs.ext.Provider;
+import jakarta.ws.rs.ext.Providers;
+
+import org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.base.ProviderBase;
+
+import tools.jackson.core.Version;
+import tools.jackson.databind.AnnotationIntrospector;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.ObjectReader;
+import tools.jackson.databind.ObjectWriter;
+import tools.jackson.databind.json.JsonMapper;
+
+/**
+ * Basic implementation of Jakarta-RS abstractions ({@link MessageBodyReader},
+ * {@link MessageBodyWriter}) needed for binding
+ * JSON ("application/json") content to and from Java Objects ("POJO"s).
+ *
+ * Actual data binding functionality is implemented by {@link ObjectMapper}:
+ * mapper to use can be configured in multiple ways:
+ *
+ * - By explicitly passing mapper to use in constructor
+ *
- By explictly setting mapper to use by {@link #setMapper}
+ *
- By defining Jakarta-RS
Provider that returns {@link ObjectMapper}s.
+ * - By doing none of above, in which case a default mapper instance is
+ * constructed (and configured if configuration methods are called)
+ *
+ * The last method ("do nothing specific") is often good enough; explicit passing
+ * of Mapper is simple and explicit; and Provider-based method may make sense
+ * with Dependency Injection frameworks, or if Mapper has to be configured differently
+ * for different media types.
+ *
+ * Note that the default mapper instance will be automatically created if
+ * one of explicit configuration methods (like {@link #configure})
+ * is called: if so, Provider-based introspection is NOT used, but the
+ * resulting Mapper is used as configured.
+ *
+ * There is also ({@link JacksonXmlBindJsonProvider}) which
+ * is configured by default to use both Jackson and Jakarta XmlBin annotations
+ * for configuration (base class when used as-is defaults to using just Jackson annotations)
+ *
+ * @author Tatu Saloranta
+ */
+@Provider
+@Consumes(MediaType.WILDCARD) // NOTE: required to support "non-standard" JSON variants
+@Produces({MediaType.APPLICATION_JSON, "text/json", MediaType.WILDCARD})
+public class JacksonJsonProvider
+ extends ProviderBase
+{
+ public final static String MIME_JAVASCRIPT = "application/javascript";
+
+ public final static String MIME_JAVASCRIPT_MS = "application/x-javascript";
+
+ /*
+ /**********************************************************************
+ /* General configuration
+ /**********************************************************************
+ */
+
+ /**
+ * JSONP function name to use for automatic JSONP wrapping, if any;
+ * if null, no JSONP wrapping is done.
+ * Note that this is the default value that can be overridden on
+ * per-endpoint basis.
+ */
+ protected String _jsonpFunctionName;
+
+ /*
+ /**********************************************************************
+ /* Context configuration
+ /**********************************************************************
+ */
+
+ /**
+ * Injectable context object used to locate configured
+ * instance of {@link JsonMapper} to use for actual
+ * serialization.
+ */
+ @Context
+ protected Providers _providers;
+
+ /*
+ /**********************************************************************
+ /* Construction
+ /**********************************************************************
+ */
+
+ /**
+ * Default constructor, usually used when provider is automatically
+ * configured to be used with JAX-RS implementation.
+ */
+ public JacksonJsonProvider() {
+ this(null, null);
+ }
+
+ public JacksonJsonProvider(JsonMapper mapper) {
+ this(mapper, null);
+ }
+
+ /**
+ * Constructor to use when a custom mapper (usually components
+ * like serializer/deserializer factories that have been configured)
+ * is to be used.
+ *
+ * @param aiOverride AnnotationIntrospector to override default with, if any
+ */
+ public JacksonJsonProvider(JsonMapper mapper,
+ AnnotationIntrospector aiOverride) {
+ super(new JsonMapperConfigurator(mapper, aiOverride));
+ }
+
+ /**
+ * Constructor for use with a custom mapperConfigurator (usually implementing
+ * some methods from MapperConfiguratorBase)
+ *
+ * @param mapperConfigurator custom mapper configurator to use
+ *
+ * @since 3.1
+ */
+ public JacksonJsonProvider(JsonMapperConfigurator mapperConfigurator) {
+ super(mapperConfigurator);
+ }
+
+ /**
+ * Method that will return version information stored in and read from jar
+ * that contains this class.
+ */
+ @Override
+ public Version version() {
+ return PackageVersion.VERSION;
+ }
+
+ /*
+ /**********************************************************************
+ /* JSON-specific configuration
+ /**********************************************************************
+ */
+
+ public void setJSONPFunctionName(String fname) {
+ _jsonpFunctionName = fname;
+ }
+
+ /*
+ /**********************************************************************
+ /* Abstract method impls
+ /**********************************************************************
+ */
+
+ /**
+ * Helper method used to check whether given media type
+ * is supported by this provider.
+ * Current implementation essentially checks to see whether
+ * {@link MediaType#getSubtype} returns "json" or something
+ * ending with "+json".
+ * Or "text/x-json" (since 2.3)
+ */
+ @Override
+ protected boolean hasMatchingMediaType(MediaType mediaType)
+ {
+ /* As suggested by Stephen D, there are 2 ways to check: either
+ * being as inclusive as possible (if subtype is "json"), or
+ * exclusive (major type "application", minor type "json").
+ * Let's start with inclusive one, hard to know which major
+ * types we should cover aside from "application".
+ */
+ if (mediaType != null) {
+ // Ok: there are also "xxx+json" subtypes, which count as well
+ String subtype = mediaType.getSubtype();
+
+ // [Issue#14]: also allow 'application/javascript'
+ return "json".equalsIgnoreCase(subtype) || subtype.endsWith("+json")
+ || "javascript".equals(subtype)
+ // apparently Microsoft once again has interesting alternative types?
+ || "x-javascript".equals(subtype)
+ || "x-json".equals(subtype) // [Issue#40]
+ ;
+ }
+ // Not sure if this can happen; but it seems reasonable
+ // that we can at least produce JSON without media type?
+ return true;
+ }
+
+ @Override
+ protected JsonMapper _locateMapperViaProvider(Class> type, MediaType mediaType)
+ {
+ JsonMapper m = _mapperConfig.getConfiguredMapper();
+ if (m == null) {
+ if (_providers != null) {
+ ContextResolver resolver = _providers.getContextResolver(JsonMapper.class, mediaType);
+ /* Above should work as is, but due to this bug
+ * [https://jersey.dev.java.net/issues/show_bug.cgi?id=288]
+ * in Jersey, it doesn't. But this works until resolution of
+ * the issue:
+ */
+ if (resolver == null) {
+ resolver = _providers.getContextResolver(JsonMapper.class, null);
+ }
+ if (resolver != null) {
+ return resolver.getContext(type);
+ }
+ }
+ if (m == null) {
+ // If not, let's get the fallback default instance
+ m = _mapperConfig.getDefaultMapper();
+ }
+ }
+ return m;
+ }
+
+ @Override
+ protected JsonEndpointConfig _configForReading(ObjectReader reader,
+ Annotation[] annotations) {
+ return JsonEndpointConfig.forReading(reader, annotations);
+ }
+
+ @Override
+ protected JsonEndpointConfig _configForWriting(ObjectWriter writer,
+ Annotation[] annotations) {
+ return JsonEndpointConfig.forWriting(writer, annotations,
+ _jsonpFunctionName);
+ }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JacksonXmlBindJsonProvider.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JacksonXmlBindJsonProvider.java
new file mode 100644
index 00000000000..3232631b7bb
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JacksonXmlBindJsonProvider.java
@@ -0,0 +1,67 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.ext.Provider;
+
+import tools.jackson.databind.AnnotationIntrospector;
+import tools.jackson.databind.json.JsonMapper;
+import tools.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationIntrospector;
+
+/**
+ * JSON content type provider automatically configured to use both Jackson
+ * and Jakarta XmlBind annotations (in that order of priority). Otherwise functionally
+ * same as {@link JacksonJsonProvider}.
+ *
+ * Typical usage pattern is to just instantiate instance of this
+ * provider for Jakarta-RS and use as is: this will use both Jackson and
+ * Jakarta XmlBind annotations (with Jackson annotations having priority).
+ *
+ * Note: class annotations are duplicated from super class, since it
+ * is not clear whether Jakarta-RS implementations are required to
+ * check settings of super-classes. It is important to keep annotations
+ * in sync if changed.
+ */
+@Provider
+@Consumes(MediaType.WILDCARD) // NOTE: required to support "non-standard" JSON variants
+@Produces({MediaType.APPLICATION_JSON, "text/json", MediaType.WILDCARD})
+public class JacksonXmlBindJsonProvider extends JacksonJsonProvider
+{
+ /**
+ * Default constructor, usually used when provider is automatically
+ * configured to be used with Jakarta-RS implementation.
+ */
+ public JacksonXmlBindJsonProvider() {
+ this(null, JaxbHolder.get());
+ }
+
+ /**
+ * Constructor to use when a custom mapper (usually components
+ * like serializer/deserializer factories that have been configured)
+ * is to be used.
+ */
+ public JacksonXmlBindJsonProvider(JsonMapper mapper,
+ AnnotationIntrospector aiOverride)
+ {
+ super(mapper, aiOverride);
+ }
+
+ /**
+ * Constructor for use with a custom mapperConfigurator (usually implementing
+ * some methods from MapperConfiguratorBase)
+ * @since 3.1
+ */
+ public JacksonXmlBindJsonProvider(JsonMapperConfigurator mapperConfigurator) {
+ super(mapperConfigurator);
+ }
+
+ // Silly class to encapsulate reference to JAXB introspector class so that
+ // loading of parent class does not require it; only happens if and when
+ // introspector needed
+ private static class JaxbHolder {
+ public static AnnotationIntrospector get() {
+ return new JakartaXmlBindAnnotationIntrospector();
+ }
+ }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JsonEndpointConfig.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JsonEndpointConfig.java
new file mode 100644
index 00000000000..fc1c4869eb3
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JsonEndpointConfig.java
@@ -0,0 +1,104 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json;
+
+import org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg.EndpointConfigBase;
+import org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json.annotation.JSONP;
+
+import tools.jackson.databind.ObjectReader;
+import tools.jackson.databind.ObjectWriter;
+import tools.jackson.databind.cfg.MapperConfig;
+import tools.jackson.databind.util.JSONPObject;
+import tools.jackson.databind.util.JSONWrappedObject;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Container class for figuring out annotation-based configuration
+ * for Jakarta-RS end points.
+ */
+public class JsonEndpointConfig
+ extends EndpointConfigBase
+{
+ // // Serialization-only config
+
+ protected JSONP.Def _jsonp;
+
+ /*
+ /**********************************************************
+ /* Construction
+ /**********************************************************
+ */
+
+ protected JsonEndpointConfig(MapperConfig> config) {
+ super(config);
+ }
+
+ public static JsonEndpointConfig forReading(ObjectReader reader,
+ Annotation[] annotations)
+ {
+ return new JsonEndpointConfig(reader.getConfig())
+ .add(annotations, false)
+ .initReader(reader);
+ }
+
+ public static JsonEndpointConfig forWriting(ObjectWriter writer,
+ Annotation[] annotations,
+ String defaultJsonpMethod)
+ {
+ JsonEndpointConfig config = new JsonEndpointConfig(writer.getConfig());
+ if (defaultJsonpMethod != null) {
+ config._jsonp = new JSONP.Def(defaultJsonpMethod);
+ }
+ return config
+ .add(annotations, true)
+ .initWriter(writer)
+ ;
+ }
+
+ /*
+ /**********************************************************
+ /* Abstract method impls, overrides
+ /**********************************************************
+ */
+
+ @Override
+ protected void addAnnotation(Class extends Annotation> type,
+ Annotation annotation, boolean forWriting)
+ {
+ if (type == JSONP.class) {
+ if (forWriting) {
+ _jsonp = new JSONP.Def((JSONP) annotation);
+ }
+ } else {
+ super.addAnnotation(type, annotation, forWriting);
+ }
+ }
+
+ @Override
+ public Object modifyBeforeWrite(Object value) {
+ return applyJSONP(value);
+ }
+
+ /*
+ /**********************************************************
+ /* Accessors
+ /**********************************************************
+ */
+
+ /**
+ * Method that will add JSONP wrapper object, if and as
+ * configured by collected annotations.
+ */
+ public Object applyJSONP(Object value)
+ {
+ if (_jsonp != null) {
+ // full prefix+suffix?
+ if (_jsonp.prefix != null || _jsonp.suffix != null) {
+ return new JSONWrappedObject(_jsonp.prefix, _jsonp.suffix, value);
+ }
+ if (_jsonp.method != null) {
+ return new JSONPObject(_jsonp.method, value);
+ }
+ }
+ return value;
+ }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JsonMapperConfigurator.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JsonMapperConfigurator.java
new file mode 100644
index 00000000000..43f7011b0fe
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/JsonMapperConfigurator.java
@@ -0,0 +1,35 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json;
+
+import org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg.MapperConfiguratorBase;
+
+import tools.jackson.databind.AnnotationIntrospector;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.databind.cfg.MapperBuilder;
+import tools.jackson.databind.json.JsonMapper;
+
+/**
+ * Helper class used to encapsulate details of configuring an
+ * {@link ObjectMapper} instance to be used for data binding, as
+ * well as accessing it.
+ */
+public class JsonMapperConfigurator
+ extends MapperConfiguratorBase
+{
+ public JsonMapperConfigurator(JsonMapper mapper,
+ AnnotationIntrospector aiOverride)
+ {
+ super(mapper, aiOverride);
+ }
+
+ /*
+ /**********************************************************************
+ /* Abstract method impls
+ /**********************************************************************
+ */
+
+ @Override
+ protected MapperBuilder,?> mapperBuilder() {
+ return JsonMapper.builder();
+ }
+}
+
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/PackageVersion.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/PackageVersion.java
new file mode 100644
index 00000000000..6d95425700d
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/PackageVersion.java
@@ -0,0 +1,20 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json;
+
+import tools.jackson.core.Version;
+import tools.jackson.core.Versioned;
+import tools.jackson.core.util.VersionUtil;
+
+/**
+ * Automatically generated from PackageVersion.java.in during
+ * packageVersion-generate execution of maven-replacer-plugin in
+ * pom.xml.
+ */
+public final class PackageVersion implements Versioned {
+ public static final Version VERSION = VersionUtil.parseVersion(
+ "3.0.4", "tools.jackson.jakarta.rs", "jackson-jakarta-rs-json-provider");
+
+ @Override
+ public Version version() {
+ return VERSION;
+ }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/annotation/JSONP.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/annotation/JSONP.java
new file mode 100644
index 00000000000..5bd49d5a84c
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/annotation/JSONP.java
@@ -0,0 +1,95 @@
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Note: applicable to annotations to allow bundling (if support added
+ * to Jakarta-RS bundle itself), as well as methods to indicate that return
+ * type is to be wrapped.
+ * Other types are not allowed, since there is no current usage for those;
+ * input can't be wrapped (so no need for parameters); fields are not
+ * exposed through Jakarta-RS; and we do not allow 'default wrapping' for
+ * types.
+ *
+ * Note on properties: if either {@link #prefix()} or {@link #suffix()}
+ * is non-empty, they are used as literal prefix and suffix to use.
+ * Otherwise {@link #value()} is used as the function name, followed
+ * by opening parenthesis, value, and closing parenthesis.
+ *
+ * Example usage:
+ *
+ * class Wrapper {
+ * @JSONP("myFunc") public int value = 3;
+ * }
+ *
+ * would serialize as:
+ *
+ * myFunc({"value":3})
+ *
+ * whereas
+ *
+ *
+ * class Wrapper {
+ * @JSONP(prefix="call(", suffix=")+3") public int value = 1;
+ * }
+ *
+ * would serialize as:
+ *
+ * call({"value":1})+3
+ *
+ */
+@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@com.fasterxml.jackson.annotation.JacksonAnnotation
+public @interface JSONP
+{
+ /**
+ * Method used for JSONP, unless {@link #prefix()} or
+ * {@link #suffix()} return non-empty Strings.
+ */
+ public String value() default "";
+
+ /**
+ * Prefix String used for JSONP if not empty: will be included
+ * verbatim before JSON value.
+ */
+ public String prefix() default "";
+
+ /**
+ * Suffix String used for JSONP if not empty: will be included
+ * verbatim after JSON value.
+ */
+ public String suffix() default "";
+
+ /**
+ * Helper class for encapsulating information from {@link JSONP}
+ * annotation instance.
+ */
+ public static class Def {
+ public final String method;
+ public final String prefix;
+ public final String suffix;
+
+ public Def(String m) {
+ method = m;
+ prefix = null;
+ suffix = null;
+ }
+
+ public Def(JSONP json) {
+ method = emptyAsNull(json.value());
+ prefix = emptyAsNull(json.prefix());
+ suffix = emptyAsNull(json.suffix());
+ }
+
+ private final static String emptyAsNull(String str) {
+ if (str == null || str.length() == 0) {
+ return null;
+ }
+ return str;
+ }
+ }
+}
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/annotation/package-info.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/annotation/package-info.java
new file mode 100644
index 00000000000..ba18c3c7375
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/annotation/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * Package that contains annotations specific to JSON dataformat.
+ *
+ * NOTE: starting with version 2.2, general-purpose annotations
+ * will be moved to a shared package, and this package will only
+ * contains JSON-specific annotations.
+ */
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json.annotation;
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/package-info.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/package-info.java
new file mode 100644
index 00000000000..69643cbe021
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/internal/jackson/jakarta/rs/json/package-info.java
@@ -0,0 +1,21 @@
+/**
+ * Jackson-based Jakarta-RS provider that can automatically
+ * serialize and deserialize resources for
+ * JSON content type (MediaType).
+ *
+ * Also continues supporting functionality, such as
+ * exception mappers that can simplify handling of
+ * error conditions.
+ *
+ * There are two default provider classes:
+ *
+ * - {@link org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json.JacksonJsonProvider} is the basic
+ * provider configured to use Jackson annotations
+ *
+ * - {@link org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json.JacksonXmlBindJsonProvider} is extension
+ * of the basic provider, configured to additionally use JAXB annotations,
+ * in addition to (or in addition of, if so configured) Jackson annotations.
+ *
+ *
+ */
+package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json;
diff --git a/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/package-info.java b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/package-info.java
new file mode 100644
index 00000000000..ace9e239fc8
--- /dev/null
+++ b/media/json-jackson3/src/main/java/org/glassfish/jersey/jackson3/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013, 2018, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jersey classes supporting JSON media type processing based on Jackson 3.
+ */
+package org.glassfish.jersey.jackson3;
diff --git a/media/json-jackson3/src/main/resources/META-INF/NOTICE.markdown b/media/json-jackson3/src/main/resources/META-INF/NOTICE.markdown
new file mode 100644
index 00000000000..7b1f957d2b9
--- /dev/null
+++ b/media/json-jackson3/src/main/resources/META-INF/NOTICE.markdown
@@ -0,0 +1,37 @@
+# Notice for Jersey Json Jackson3 module
+This content is produced and maintained by the Eclipse Jersey project.
+
+* https://projects.eclipse.org/projects/ee4j.jersey
+
+## Trademarks
+Eclipse Jersey is a trademark of the Eclipse Foundation.
+
+## Copyright
+
+All content is the property of the respective authors or their employers. For
+more information regarding authorship of content, please consult the listed
+source code repository logs.
+
+## Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License v. 2.0 which is available at
+http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made
+available under the following Secondary Licenses when the conditions for such
+availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU
+General Public License, version 2 with the GNU Classpath Exception which is
+available at https://www.gnu.org/software/classpath/license.html.
+
+SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+## Source Code
+The project maintains the following source code repositories:
+
+* https://github.com/eclipse-ee4j/jersey
+
+## Third-party Content
+
+Jackson JAX-RS Providers version 3.0.4
+* License: Apache License, 2.0
+* Project: https://github.com/FasterXML/jackson-jakarta-rs-providers
+* Copyright: (c) 2009-2025 FasterXML, LLC. All rights reserved unless otherwise indicated.
diff --git a/media/json-jackson3/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable b/media/json-jackson3/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
new file mode 100644
index 00000000000..cf5b4a4f4b5
--- /dev/null
+++ b/media/json-jackson3/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.AutoDiscoverable
@@ -0,0 +1 @@
+org.glassfish.jersey.jackson3.internal.JacksonAutoDiscoverable
diff --git a/media/json-jackson3/src/main/resources/org/glassfish/jersey/jackson3/localization.properties b/media/json-jackson3/src/main/resources/org/glassfish/jersey/jackson3/localization.properties
new file mode 100644
index 00000000000..a5d50c6f8f8
--- /dev/null
+++ b/media/json-jackson3/src/main/resources/org/glassfish/jersey/jackson3/localization.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (c) 2023, 2025, 2026 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v. 2.0, which is available at
+# http://www.eclipse.org/legal/epl-2.0.
+#
+# This Source Code may also be made available under the following Secondary
+# Licenses when the conditions for such availability set forth in the
+# Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+# version 2 with the GNU Classpath Exception, which is available at
+# https://www.gnu.org/software/classpath/license.html.
+#
+# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+#
+error.configuring=Error configuring the DefaultJacksonJaxbJsonProvider: {0}.
+error.modules.not.loaded=Jackson modules could not be loaded: {0}
\ No newline at end of file
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJacksonXmlBindJsonProviderTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJacksonXmlBindJsonProviderTest.java
new file mode 100644
index 00000000000..9309a9786fb
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJacksonXmlBindJsonProviderTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020, 2022, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson3.JacksonFeature;
+import org.glassfish.jersey.jackson3.internal.model.NoCtorPojo;
+import org.glassfish.jersey.jackson3.internal.model.ServiceTest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.core.Application;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public final class DefaultJacksonXmlBindJsonProviderTest extends JerseyTest {
+
+ @Override
+ protected final Application configure() {
+ return new ResourceConfig(ServiceTest.class)
+ .property(CommonProperties.JSON_JACKSON_DISABLED_MODULES, "NoCtorDeserModule");
+ }
+
+ // Exclude NoCtorDeserModule because of dependencies
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.property(CommonProperties.JSON_JACKSON_DISABLED_MODULES, "NoCtorDeserModule");
+ }
+
+ @Test
+ public final void testSimpleGet() {
+ final ServiceTest.EntityTest response = target("entity/simple").request().get(ServiceTest.EntityTest.class);
+ assertEquals("Hello", response.getName());
+ assertEquals("World", response.getValue().orElse(""));
+ }
+
+ @Test
+ public final void testSimplePost() {
+ Entity request = Entity.json(new ServiceTest.EntityTest("Hello", "World"));
+ try (Response response = target("entity/exchange").request(MediaType.APPLICATION_JSON).post(request)) {
+ assertEquals(200, response.getStatus());
+ ServiceTest.EntityTest responseEntity = response.readEntity(ServiceTest.EntityTest.class);
+ assertEquals("Hello", responseEntity.getName());
+ assertEquals("Universe", responseEntity.getValue().orElse(""));
+ }
+ }
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForAnotherEnabledModuleTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForAnotherEnabledModuleTest.java
new file mode 100644
index 00000000000..3dbd16eb191
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForAnotherEnabledModuleTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.Providers;
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson3.JacksonFeature;
+import org.glassfish.jersey.jackson3.internal.model.NoCtorPojo;
+import org.glassfish.jersey.jackson3.internal.model.NoCtorServiceTest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import tools.jackson.core.JacksonException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+// Test to verify that enabling one module will remove others. Should not able to deserialize NoCtorResponse
+public class DefaultJsonJacksonProviderForAnotherEnabledModuleTest extends JerseyTest {
+
+ @Override
+ protected final Application configure() {
+ return new ResourceConfig(NoCtorServiceTest.class)
+ .property(CommonProperties.JSON_JACKSON_ENABLED_MODULES, "MrBeanModule");
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(JacksonFeature.class)
+ .register(TestJacksonXmlBindJsonProvider.class)
+ .property(CommonProperties.JSON_JACKSON_ENABLED_MODULES, "MrBeanModule");
+ }
+
+ @Test
+ public final void testAnotherEnabledModule() {
+ try (Response response = target("entity/simple").request().get()) {
+ assertEquals(200, response.getStatus());
+ assertThrows(JacksonException.class, () -> response.readEntity(NoCtorPojo.class));
+ }
+ }
+
+ @Test
+ public final void testExchangeAnotherEnabledModule() {
+ Entity request = Entity.json(new NoCtorPojo("Hello", "World"));
+ try (Response response = target("entity/exchange").request(MediaType.APPLICATION_JSON).post(request)) {
+ assertEquals(400, response.getStatus());
+ assertTrue(response.readEntity(String.class).startsWith("Cannot construct instance of"));
+ }
+ }
+
+
+ static class TestJacksonXmlBindJsonProvider extends DefaultJacksonXmlBindJsonProvider {
+
+ private final Configuration configuration;
+
+ @Inject
+ public TestJacksonXmlBindJsonProvider(@Context Providers providers, @Context Configuration configuration) {
+ super(providers, configuration);
+ this.configuration = configuration;
+ }
+
+ @PostConstruct
+ public void checkModulesCount() {
+ final String enabledModules =
+ CommonProperties.getValue(configuration.getProperties(),
+ configuration.getRuntimeType(),
+ CommonProperties.JSON_JACKSON_ENABLED_MODULES, String.class);
+ assertEquals("MrBeanModule", enabledModules);
+ }
+
+ }
+
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java
new file mode 100644
index 00000000000..b99b55a7e84
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForDisabledModulesTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.Providers;
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson3.JacksonFeature;
+import org.glassfish.jersey.jackson3.internal.model.NoCtorPojo;
+import org.glassfish.jersey.jackson3.internal.model.NoCtorServiceTest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+import tools.jackson.databind.DatabindException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+// Test to verify that disabled modules are registered. Should not able to deserialize NoCtorResponse
+public class DefaultJsonJacksonProviderForDisabledModulesTest extends JerseyTest {
+
+ @Override
+ protected final Application configure() {
+ return new ResourceConfig(NoCtorServiceTest.class)
+ .property(CommonProperties.JSON_JACKSON_DISABLED_MODULES, "NoCtorDeserModule");
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(JacksonFeature.class)
+ .register(TestJacksonXmlBindJsonProvider.class)
+ .property(CommonProperties.JSON_JACKSON_DISABLED_MODULES, "NoCtorDeserModule");
+ }
+
+ @Test
+ public final void testDisabledModule() {
+ try (Response response = target("entity/simple").request().get()) {
+ assertEquals(200, response.getStatus());
+ assertThrows(DatabindException.class, () -> response.readEntity(NoCtorPojo.class));
+ }
+ }
+
+ @Test
+ public final void testExchangeDisabledModule() {
+ Entity request = Entity.json(new NoCtorPojo("Hello", "World"));
+ try (Response response = target("entity/exchange").request(MediaType.APPLICATION_JSON).post(request)) {
+ assertEquals(400, response.getStatus());
+ assertTrue(response.readEntity(String.class).startsWith("Cannot construct instance of"));
+ }
+ }
+
+
+ private static class TestJacksonXmlBindJsonProvider extends DefaultJacksonXmlBindJsonProvider {
+
+ private final Configuration configuration;
+
+ @Inject
+ public TestJacksonXmlBindJsonProvider(@Context Providers providers, @Context Configuration configuration) {
+ super(providers, configuration);
+ this.configuration = configuration;
+ }
+
+ @PostConstruct
+ public void checkModulesCount() {
+ final String disabledModules =
+ CommonProperties.getValue(configuration.getProperties(),
+ configuration.getRuntimeType(),
+ CommonProperties.JSON_JACKSON_DISABLED_MODULES, String.class);
+ assertEquals("NoCtorDeserModule", disabledModules);
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java
new file mode 100644
index 00000000000..90e9ed6cbd4
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForEnabledModulesTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.Providers;
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson3.JacksonFeature;
+import org.glassfish.jersey.jackson3.internal.model.NoCtorPojo;
+import org.glassfish.jersey.jackson3.internal.model.NoCtorServiceTest;
+import org.glassfish.jersey.message.MessageProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+import tools.jackson.core.StreamReadConstraints;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+// Test to verify that enabled modules are registered. Should be able to deserialize NoCtorResponse
+public class DefaultJsonJacksonProviderForEnabledModulesTest extends JerseyTest {
+
+ @Override
+ protected final Application configure() {
+ return new ResourceConfig(NoCtorServiceTest.class)
+ .property(CommonProperties.JSON_JACKSON_ENABLED_MODULES, "NoCtorDeserModule");
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(JacksonFeature.class)
+ .register(TestJacksonXmlBindJsonProvider.class)
+ .property(CommonProperties.JSON_JACKSON_ENABLED_MODULES, "NoCtorDeserModule");
+ }
+
+
+ @Test
+ public final void testEnabledModule() {
+ try (Response response = target("entity/simple").request().get()) {
+ assertEquals(200, response.getStatus());
+ NoCtorPojo responseEntity = response.readEntity(NoCtorPojo.class);
+ assertEquals("Hello", responseEntity.name);
+ assertEquals("World", responseEntity.value);
+ }
+ }
+
+ @Test
+ public final void testExchangeEnabledModule() {
+ Entity request = Entity.json(new NoCtorPojo("Hello", "World"));
+ try (Response response = target("entity/exchange").request(MediaType.APPLICATION_JSON).post(request)) {
+ assertEquals(200, response.getStatus());
+ NoCtorPojo responseEntity = response.readEntity(NoCtorPojo.class);
+ assertEquals("Howdy", responseEntity.name);
+ assertEquals("World", responseEntity.value);
+ }
+ }
+
+ static class TestJacksonXmlBindJsonProvider extends DefaultJacksonXmlBindJsonProvider {
+
+ private final Configuration configuration;
+
+ @Inject
+ public TestJacksonXmlBindJsonProvider(@Context Providers providers, @Context Configuration configuration) {
+ super(providers, configuration);
+ this.configuration = configuration;
+ }
+
+ @PostConstruct
+ public void checkModulesCount() {
+ final String enabledModules =
+ CommonProperties.getValue(configuration.getProperties(),
+ configuration.getRuntimeType(),
+ CommonProperties.JSON_JACKSON_ENABLED_MODULES, String.class);
+ assertEquals("NoCtorDeserModule", enabledModules);
+ }
+
+ }
+
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForNoModulePropertiesTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForNoModulePropertiesTest.java
new file mode 100644
index 00000000000..f77f04ea656
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/DefaultJsonJacksonProviderForNoModulePropertiesTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson3.JacksonFeature;
+import org.glassfish.jersey.jackson3.internal.model.NoCtorPojo;
+import org.glassfish.jersey.jackson3.internal.model.NoCtorServiceTest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+// Test will succeed since NoCtorDeserModule is imported with test scope
+public class DefaultJsonJacksonProviderForNoModulePropertiesTest extends JerseyTest {
+
+ @Override
+ protected final Application configure() {
+ return new ResourceConfig(NoCtorServiceTest.class);
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(JacksonFeature.class);
+ }
+
+ @Test
+ public final void testModuleNotSpecified() {
+ try (Response response = target("entity/simple").request().get()) {
+ assertEquals(200, response.getStatus());
+ NoCtorPojo responseEntity = response.readEntity(NoCtorPojo.class);
+ assertEquals("Hello", responseEntity.name);
+ assertEquals("World", responseEntity.value);
+ }
+ }
+
+ @Test
+ public final void testExchangeModuleNotSpecified() {
+ Entity request = Entity.json(new NoCtorPojo("Hello", "World"));
+ try (Response response = target("entity/exchange").request(MediaType.APPLICATION_JSON).post(request)) {
+ assertEquals(200, response.getStatus());
+ NoCtorPojo responseEntity = response.readEntity(NoCtorPojo.class);
+ assertEquals("Howdy", responseEntity.name);
+ assertEquals("World", responseEntity.value);
+ }
+ }
+
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/JacksonJaxb2JsonProviderTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/JacksonJaxb2JsonProviderTest.java
new file mode 100644
index 00000000000..6de83bcaeba
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/JacksonJaxb2JsonProviderTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson3.internal.model.Jaxb2ServiceTest;
+import org.glassfish.jersey.jackson3.internal.model.ServiceTest;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public final class JacksonJaxb2JsonProviderTest extends JerseyTest {
+
+ @Override
+ protected final Application configure() {
+ return new ResourceConfig(Jaxb2ServiceTest.class)
+ .property(CommonProperties.JSON_JACKSON_DISABLED_MODULES, "NoCtorDeserModule");
+ }
+
+ // Exclude NoCtorDeserModule because of dependencies
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.property(CommonProperties.JSON_JACKSON_DISABLED_MODULES, "NoCtorDeserModule");
+ }
+
+ @Test
+ public final void testJavaOptional() {
+ final String response = target("entity/simple").request().get(String.class);
+ assertEquals("{\"name\":\"Hello\",\"value\":\"World\"}", response);
+ }
+
+ @Test
+ public final void testSimpleGet() {
+ final Jaxb2ServiceTest.EntityTest response = target("entity/simple").request().get(Jaxb2ServiceTest.EntityTest.class);
+ assertEquals("Hello", response.getName());
+ assertEquals("World", response.getValue().orElse(""));
+ }
+
+ @Test
+ public final void testSimplePost() {
+ Entity request = Entity.json(new Jaxb2ServiceTest.EntityTest("Hello", "World"));
+ try (Response response = target("entity/exchange").request(MediaType.APPLICATION_JSON).post(request)) {
+ assertEquals(200, response.getStatus());
+ Jaxb2ServiceTest.EntityTest responseEntity = response.readEntity(Jaxb2ServiceTest.EntityTest.class);
+ assertEquals("Hello", responseEntity.getName());
+ assertEquals("Universe", responseEntity.getValue().orElse(""));
+ }
+ }
+
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/JakartaRSFeatureTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/JakartaRSFeatureTest.java
new file mode 100644
index 00000000000..58a3ea71207
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/JakartaRSFeatureTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2024, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ContextResolver;
+import jakarta.ws.rs.ext.Providers;
+import org.glassfish.jersey.jackson3.JacksonFeature;
+import org.glassfish.jersey.jackson3.JakartaRSFeatureJsonMapper;
+import org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg.JakartaRSFeature;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+import tools.jackson.databind.json.JsonMapper;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+public class JakartaRSFeatureTest {
+ @Test
+ public void testJakartaRsFeatureOnJacksonFeature() {
+ Client client = ClientBuilder.newClient()
+ .register(new JacksonFeature().jakartaRSFeature(JakartaRSFeature.READ_FULL_STREAM, false))
+ .register(JakartaRsFeatureFilter.class);
+
+ try (Response r = client.target("http://xxx.yyy").request().get()) {
+ var s = r.readEntity(String.class);
+ MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
+ }
+ }
+
+ @Test
+ public void testJakartaRsFeatureOnContextResolver() {
+ Client client = ClientBuilder.newClient()
+ .register(JacksonFeature.class)
+ .register(JakartaRsFeatureContextResolver.class)
+ .register(JakartaRsFeatureFilter.class);
+
+ try (Response r = client.target("http://xxx.yyy").request().get()) {
+ MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
+ }
+ }
+
+
+ public static class JakartaRsFeatureFilter implements ClientRequestFilter {
+ private final DefaultJacksonXmlBindJsonProvider jacksonProvider;
+ @Inject
+ public JakartaRsFeatureFilter(Providers allProviders) {
+ jacksonProvider = (DefaultJacksonXmlBindJsonProvider)
+ allProviders.getMessageBodyReader(Object.class, Object.class, null, MediaType.APPLICATION_JSON_TYPE);
+ try {
+ jacksonProvider.readFrom(Object.class, Object.class, null, MediaType.APPLICATION_JSON_TYPE, null,
+ new ByteArrayInputStream("{}".getBytes()));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ };
+
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ Response.Status status = jacksonProvider.isEnabled(JakartaRSFeature.READ_FULL_STREAM)
+ ? Response.Status.FORBIDDEN
+ : Response.Status.OK;
+ requestContext.abortWith(Response.status(status).build());
+ }
+ }
+
+ public static class JakartaRsFeatureContextResolver implements ContextResolver {
+
+ @Override
+ public JsonMapper getContext(Class> type) {
+ JakartaRSFeatureJsonMapper jsonMapper = new JakartaRSFeatureJsonMapper();
+ jsonMapper.disable(JakartaRSFeature.READ_FULL_STREAM);
+ return jsonMapper;
+ }
+ }
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/StreamReadConstrainsTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/StreamReadConstrainsTest.java
new file mode 100644
index 00000000000..c166321d66a
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/StreamReadConstrainsTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2023, 2024, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal;
+
+import com.fasterxml.jackson.annotation.JsonGetter;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.Produces;
+import tools.jackson.core.json.JsonFactory;
+import tools.jackson.core.StreamReadConstraints;
+import tools.jackson.core.Version;
+import tools.jackson.core.exc.StreamConstraintsException;
+import tools.jackson.core.json.PackageVersion;
+import tools.jackson.databind.JsonNode;
+import tools.jackson.databind.json.JsonMapper;
+import tools.jackson.databind.node.StringNode;
+
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ContextResolver;
+import jakarta.ws.rs.ext.ExceptionMapper;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson3.JacksonFeature;
+import org.glassfish.jersey.message.MessageProperties;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Method;
+
+public class StreamReadConstrainsTest extends JerseyTest {
+
+ private static final String ERROR_MSG_PART = "maximum allowed (";
+
+ @Override
+ protected final Application configure() {
+ return new ResourceConfig(TestLengthResource.class,
+ MyStreamReadConstraints.class,
+ MyStreamReadConstraintsExceptionMapper.class);
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(JacksonFeature.class);
+ }
+
+ @Test
+ void testNumberLength() {
+ try (Response response = target("len/entity").request()
+ .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON_TYPE)
+ .post(Entity.entity(new MyEntity(3), MediaType.APPLICATION_JSON_TYPE))) {
+ Assertions.assertEquals(200, response.getStatus());
+ JsonNode entity = response.readEntity(JsonNode.class);
+ Assertions.assertEquals("1234", entity.get("value").asText());
+ }
+
+ try (Response response = target("len/entity").request()
+ .post(Entity.entity(new MyEntity(8), MediaType.APPLICATION_JSON_TYPE))) {
+ Assertions.assertEquals(200, response.getStatus());
+ String errorMsg = response.readEntity(String.class);
+ Assertions.assertTrue(errorMsg.contains(ERROR_MSG_PART + 4));
+ }
+ }
+
+ @Test
+ void testStringLengthUsingProperty() {
+ testConstraintOnClient(
+ client()
+ .property(MessageProperties.JSON_MAX_STRING_LENGTH, 4)
+ .target(getBaseUri())
+ .path("len/strlen"),
+ 4
+ );
+ }
+
+ @Test
+ void testStringLengthPriorityProperty() {
+ testConstraintOnClient(
+ ClientBuilder.newClient()
+ .register(JacksonFeature.withExceptionMappers().maxStringLength(30))
+ .property(MessageProperties.JSON_MAX_STRING_LENGTH, "3" /* check string value */)
+ .target(getBaseUri()).path("len/strlen"),
+ 3);
+ }
+
+ @Test
+ void testStringLengthUsingFeature() {
+ testConstraintOnClient(
+ ClientBuilder.newClient()
+ .register(JacksonFeature.withExceptionMappers().maxStringLength(3))
+ .target(getBaseUri())
+ .path("len/strlen"),
+ 3
+ );
+ }
+
+ void testConstraintOnClient(WebTarget target, int expectedLength) {
+ try (Response response = target.request().post(Entity.entity(expectedLength + 1, MediaType.APPLICATION_JSON_TYPE))) {
+ Assertions.assertEquals(200, response.getStatus());
+
+ JsonNode errorMsg = response.readEntity(JsonNode.class);
+ Assertions.fail("StreamConstraintsException has not been thrown");
+ } catch (StreamConstraintsException ex) {
+ if (!StreamConstraintsException.class.isInstance(ex)) {
+ throw ex;
+ }
+ String errorMsg = ex.getMessage();
+ Assertions.assertTrue(errorMsg.contains(ERROR_MSG_PART + String.valueOf(expectedLength)));
+ }
+ StreamReadConstraints.Builder builder = StreamReadConstraints.builder()
+ .maxStringLength(StreamReadConstraints.DEFAULT_MAX_STRING_LEN);
+ StreamReadConstraints.overrideDefaultStreamReadConstraints(builder.build());
+ }
+
+ @Test
+ void testMatchingVersion() {
+ final Version coreVersion = PackageVersion.VERSION;
+ final Version jerseyVersion = org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.json.PackageVersion.VERSION;
+
+ StringBuilder message = new StringBuilder();
+ message.append("Dependency Jackson Version is ")
+ .append(coreVersion.getMajorVersion())
+ .append(".")
+ .append(coreVersion.getMinorVersion());
+ message.append("\n Repackaged Jackson Version is ")
+ .append(jerseyVersion.getMajorVersion())
+ .append(".")
+ .append(jerseyVersion.getMinorVersion());
+
+ Assertions.assertEquals(coreVersion.getMajorVersion(), jerseyVersion.getMajorVersion(), message.toString());
+ Assertions.assertEquals(coreVersion.getMinorVersion(), jerseyVersion.getMinorVersion(), message.toString());
+ Assertions.assertEquals(coreVersion.getMajorVersion(), 3,
+ "update " + DefaultJacksonXmlBindJsonProvider.class.getName()
+ + " updateFactoryConstraints method to support version " + coreVersion.getMajorVersion());
+ }
+
+ @Test
+ void testStreamReadConstraintsMethods() {
+ String message = "There are additional methods in Jackson's StreamReaderConstraints.Builder."
+ + " Please update the code in " + DefaultJacksonXmlBindJsonProvider.class.getName()
+ + " updateFactoryConstraints method";
+ Method[] method = StreamReadConstraints.Builder.class.getDeclaredMethods();
+ // 2.17 : five setMax... + build() methods
+ // 2.18 : six setMax... + build() methods
+ Assertions.assertEquals(7, method.length, message);
+ }
+
+ @Path("len")
+ public static class TestLengthResource {
+ @POST
+ @Path("number")
+ @Produces(MediaType.APPLICATION_JSON)
+ public MyEntity number(int len) {
+ return new MyEntity(len);
+ }
+
+ @POST
+ @Path("strlen")
+ @Produces(MediaType.APPLICATION_JSON)
+ public JsonNode string(int len) {
+ return new StringNode(String.valueOf(new MyEntity(len).getValue()));
+ }
+
+
+ @POST
+ @Path("entity")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public MyEntity number(MyEntity entity) {
+ return new MyEntity(4);
+ }
+ }
+
+ static class MyEntity {
+
+ private int value;
+
+ // For Jackson
+ MyEntity() {
+ }
+
+ MyEntity(int length) {
+ int val = 0;
+ for (int i = 1, j = 1; i != length + 1; i++, j++) {
+ if (j == 10) {
+ j = 0;
+ }
+ val = 10 * val + j;
+ }
+ this.value = val;
+ }
+
+ @JsonGetter("value")
+ public int getValue() {
+ return value;
+ }
+ }
+
+ static class MyStreamReadConstraintsExceptionMapper implements ExceptionMapper {
+
+ @Override
+ public Response toResponse(StreamConstraintsException exception) {
+ return Response.ok().entity(exception.getMessage()).build();
+ }
+ }
+
+ static class MyStreamReadConstraints implements ContextResolver {
+
+ @Override
+ public JsonMapper getContext(Class> type) {
+ JsonFactory jsonFactory = JsonFactory.builder()
+ .streamReadConstraints(StreamReadConstraints.builder().maxNumberLength(4).build())
+ .build();
+ return JsonMapper.builder(jsonFactory).findAndAddModules().build();
+ }
+ }
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/Jaxb2ServiceTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/Jaxb2ServiceTest.java
new file mode 100644
index 00000000000..7a91855d8cd
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/Jaxb2ServiceTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2020, 2022, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal.model;
+
+import com.fasterxml.jackson.annotation.JsonGetter;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+import jakarta.xml.bind.annotation.XmlElement;
+import java.util.Optional;
+
+@Path("/entity/")
+public final class Jaxb2ServiceTest {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("/simple")
+ public final EntityTest simple() {
+ return new EntityTest("Hello", "World");
+ }
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("/exchange")
+ public EntityTest exchange(EntityTest entity) {
+ return new EntityTest(entity.getName(), "Universe");
+ }
+
+ public static final class EntityTest {
+
+ private String name;
+
+ private String value;
+
+ public EntityTest() {
+ // For deserialization without NoCtorDeserModule
+ }
+
+ public EntityTest(final String name, final String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ @XmlElement(name = "jaxb")
+ @JsonGetter("name")
+ public final String getName() {
+ return name;
+ }
+
+ @JsonGetter("value")
+ public final Optional getValue() {
+ return Optional.ofNullable(value);
+ }
+ }
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/NoCtorPojo.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/NoCtorPojo.java
new file mode 100644
index 00000000000..14f383a02bd
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/NoCtorPojo.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal.model;
+
+// Simple POJO that will fail deserialization without NoCtorDeserModule
+public class NoCtorPojo {
+
+ public String name;
+ public String value;
+
+ public NoCtorPojo(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/NoCtorServiceTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/NoCtorServiceTest.java
new file mode 100644
index 00000000000..b5cf4fb3ae3
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/NoCtorServiceTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal.model;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+
+import jakarta.ws.rs.core.MediaType;
+
+@Path("/entity/")
+public final class NoCtorServiceTest {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("/simple")
+ public NoCtorPojo simple() {
+ return new NoCtorPojo("Hello", "World");
+ }
+
+ @POST
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Path("/exchange")
+ public NoCtorPojo exchange(NoCtorPojo request) {
+ return new NoCtorPojo("Howdy", request.value);
+ }
+
+}
diff --git a/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/ServiceTest.java b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/ServiceTest.java
new file mode 100644
index 00000000000..5958d27eecf
--- /dev/null
+++ b/media/json-jackson3/src/test/java/org/glassfish/jersey/jackson3/internal/model/ServiceTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020, 2022, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jackson3.internal.model;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonGetter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.xml.bind.annotation.XmlElement;
+
+import java.util.Optional;
+
+@Path("/entity/")
+public final class ServiceTest {
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("/simple")
+ public EntityTest simple() {
+ return new EntityTest("Hello", "World");
+ }
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("/exchange")
+ public EntityTest exchange(EntityTest entity) {
+ return new EntityTest(entity.getName(), "Universe");
+ }
+
+ public static final class EntityTest {
+
+ private String name;
+ private String value;
+
+ public EntityTest() {
+ // For deserialization without NoCtorDeserModule
+ }
+
+ public EntityTest(final String name, final String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ @XmlElement(name = "jaxb")
+ @JsonGetter("name")
+ public final String getName() {
+ return name;
+ }
+
+ @JsonGetter("value")
+ public final Optional getValue() {
+ return Optional.ofNullable(value);
+ }
+ }
+
+
+
+
+}
diff --git a/media/pom.xml b/media/pom.xml
index 3d988b757b0..1b0daa60a4a 100644
--- a/media/pom.xml
+++ b/media/pom.xml
@@ -1,7 +1,7 @@
3.3.2.Final
diff --git a/tests/integration/jackson-14/pom.xml b/tests/integration/jackson-14/pom.xml
index 8be015fd342..6f0f7ed7f3a 100644
--- a/tests/integration/jackson-14/pom.xml
+++ b/tests/integration/jackson-14/pom.xml
@@ -1,7 +1,7 @@
+
+
+ 4.0.0
+
+
+ org.glassfish.jersey.tests.performance.testcases
+ project
+ ${revision}
+
+
+ json-jackson3
+ jar
+
+ jersey-tests-performance-json-jackson3-test
+
+
+ UTF-8
+
+
+
+
+
+ jakarta.ws.rs
+ jakarta.ws.rs-api
+ provided
+
+
+
+ org.glassfish.jersey.test-framework.providers
+ jersey-test-framework-provider-grizzly2
+ test
+
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-jackson3
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ **/*
+
+
+ **/*
+
+
+
+
+
+
diff --git a/tests/performance/test-cases/mbw-json-jackson3/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JakartaRsApplication.java b/tests/performance/test-cases/mbw-json-jackson3/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JakartaRsApplication.java
new file mode 100644
index 00000000000..01a543bd44b
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson3/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JakartaRsApplication.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, 2020, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.mbw.json;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import jakarta.ws.rs.core.Application;
+
+import org.glassfish.jersey.jackson3.JacksonFeature;
+
+/**
+ * Test case JAX-RS application.
+ *
+ * @author Jakub Podlesak
+ */
+public class JakartaRsApplication extends Application {
+
+ static final Set> APP_CLASSES = new HashSet>() {
+ {
+ add(JsonEntityResource.class);
+ add(JacksonFeature.class);
+ }
+ };
+
+ @Override
+ public Set> getClasses() {
+ return APP_CLASSES;
+ }
+}
diff --git a/tests/performance/test-cases/mbw-json-jackson3/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityResource.java b/tests/performance/test-cases/mbw-json-jackson3/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityResource.java
new file mode 100644
index 00000000000..b6523d3c812
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson3/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityResource.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, 2020, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.mbw.json;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PUT;
+
+/**
+ * Test resource.
+ *
+ * @author Jakub Podlesak
+ */
+@Path("/")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public class JsonEntityResource {
+
+ @POST
+ public Person echo(final Person person) {
+ return person;
+ }
+
+ @PUT
+ public void put(final Person person) {
+ }
+
+ @GET
+ public Person get() {
+ return new Person("Wolfgang", 21, "Salzburg");
+ }
+}
diff --git a/tests/performance/test-cases/mbw-json-jackson3/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/Person.java b/tests/performance/test-cases/mbw-json-jackson3/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/Person.java
new file mode 100644
index 00000000000..391fdd77ea5
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson3/src/main/java/org/glassfish/jersey/tests/performance/mbw/json/Person.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012, 2019, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.mbw.json;
+
+import java.util.Objects;
+
+/**
+ * Test data bean.
+ *
+ * @author Jakub Podlesak
+ */
+public class Person {
+
+ public String name;
+ public int age;
+ public String address;
+
+ public Person(String name, int age, String address) {
+ this.name = name;
+ this.age = age;
+ this.address = address;
+ }
+
+ public Person() {
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 19 * hash + Objects.hashCode(this.name);
+ hash = 19 * hash + this.age;
+ hash = 19 * hash + Objects.hashCode(this.address);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Person other = (Person) obj;
+ if (!Objects.equals(this.name, other.name)) {
+ return false;
+ }
+ if (this.age != other.age) {
+ return false;
+ }
+ return (Objects.equals(this.address, other.address));
+ }
+}
diff --git a/tests/performance/test-cases/mbw-json-jackson3/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/mbw-json-jackson3/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000000..dcf21a6564b
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson3/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ JakartaRsApplication
+ org.glassfish.jersey.servlet.ServletContainer
+
+ jakarta.ws.rs.Application
+ org.glassfish.jersey.tests.performance.mbw.json.JakartaRsApplication
+
+ 1
+
+
+ JakartaRsApplication
+ /*
+
+
diff --git a/tests/performance/test-cases/mbw-json-jackson3/src/test/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityTest.java b/tests/performance/test-cases/mbw-json-jackson3/src/test/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityTest.java
new file mode 100644
index 00000000000..98451c80459
--- /dev/null
+++ b/tests/performance/test-cases/mbw-json-jackson3/src/test/java/org/glassfish/jersey/tests/performance/mbw/json/JsonEntityTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, 2022, 2026 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.performance.mbw.json;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jackson3.JacksonFeature;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for json resource.
+ *
+ * @author Jakub Podlesak
+ */
+public class JsonEntityTest extends JerseyTest {
+
+ @Override
+ protected Application configure() {
+ return new JakartaRsApplication();
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(JacksonFeature.class);
+ }
+
+ @Test
+ public void testGet() {
+ final Person getResponse = target().request().get(Person.class);
+ assertEquals("Wolfgang", getResponse.name);
+ assertEquals(21, getResponse.age);
+ assertEquals("Salzburg", getResponse.address);
+ }
+
+ @Test
+ public void testPost() {
+ final Person[] testData = new Person[] {new Person("Joseph", 23, "Nazareth"), new Person("Mary", 18, "Nazareth") };
+ for (Person original : testData) {
+ final Person postResponse = target().request().post(Entity.json(original), Person.class);
+ assertEquals(original, postResponse);
+ }
+ }
+
+ @Test
+ public void testPut() {
+ final Response putResponse = target().request().put(Entity.json(new Person("Jules", 12, "Paris")));
+ assertEquals(204, putResponse.getStatus());
+ }
+}
diff --git a/tests/performance/test-cases/mbw-json-moxy/src/main/webapp/WEB-INF/web.xml b/tests/performance/test-cases/mbw-json-moxy/src/main/webapp/WEB-INF/web.xml
index 28637cdbe34..75c4ab36be2 100644
--- a/tests/performance/test-cases/mbw-json-moxy/src/main/webapp/WEB-INF/web.xml
+++ b/tests/performance/test-cases/mbw-json-moxy/src/main/webapp/WEB-INF/web.xml
@@ -1,7 +1,7 @@