Allow to use HeaderDelegateProvider to parse the response MediaType

Signed-off-by: Jan Supol <jan.supol@oracle.com>
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
index eefff1c..0f874ae 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
@@ -54,6 +54,7 @@
 
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.PropertiesDelegate;
+import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
 import org.glassfish.jersey.message.MessageBodyWorkers;
 
 /**
@@ -331,7 +332,7 @@
         }
 
         try {
-            return converter.apply(HeaderUtils.asString(value, null));
+            return converter.apply(HeaderUtils.asString(value, configuration));
         } catch (ProcessingException ex) {
             throw exception(name, value, ex);
         }
@@ -450,7 +451,9 @@
             @Override
             public MediaType apply(String input) {
                 try {
-                    return MediaType.valueOf(input);
+                    return RuntimeDelegateDecorator.configured(configuration)
+                            .createHeaderDelegate(MediaType.class)
+                            .fromString(input);
                 } catch (IllegalArgumentException iae) {
                     throw new ProcessingException(iae);
                 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
index efa30aa..3a7748c 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
@@ -267,7 +267,8 @@
      * message entity).
      */
     public MediaType getMediaType() {
-        return singleHeader(HttpHeaders.CONTENT_TYPE, MediaType.class, MediaType::valueOf, false);
+        return singleHeader(HttpHeaders.CONTENT_TYPE, MediaType.class, RuntimeDelegateDecorator.configured(configuration)
+                .createHeaderDelegate(MediaType.class)::fromString, false);
     }
 
     /**
diff --git a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java
index 38bea6e..71cc2ca 100644
--- a/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java
+++ b/tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/header/HeaderDelegateProviderTest.java
@@ -17,8 +17,12 @@
 package org.glassfish.jersey.tests.e2e.header;
 
 import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.client.ClientConfig;
 import org.glassfish.jersey.internal.ServiceFinder;
 import org.glassfish.jersey.message.internal.HeaderUtils;
+import org.glassfish.jersey.message.internal.HeaderValueException;
+import org.glassfish.jersey.message.internal.InboundMessageContext;
+import org.glassfish.jersey.message.internal.OutboundMessageContext;
 import org.glassfish.jersey.spi.HeaderDelegateProvider;
 import org.junit.Assert;
 import org.junit.Test;
@@ -34,14 +38,18 @@
 import jakarta.ws.rs.container.ContainerResponseFilter;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.MultivaluedHashMap;
 import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ReaderInterceptor;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
+import static org.junit.Assert.fail;
+
 public class HeaderDelegateProviderTest {
     static final String HEADER_NAME = "BEAN_HEADER";
     static final String DISABLED_VALUE = new BeanForHeaderDelegateProviderTest().toString();
@@ -64,6 +72,23 @@
         }
     }
 
+    public static class EmptyContentTypeHandler implements HeaderDelegateProvider<MediaType> {
+        @Override
+        public boolean supports(Class<?> type) {
+            return MediaType.class == type;
+        }
+
+        @Override
+        public MediaType fromString(String value) {
+            return value.isEmpty() ? MediaType.APPLICATION_OCTET_STREAM_TYPE : MediaType.valueOf(value);
+        }
+
+        @Override
+        public String toString(MediaType value) {
+            return value.toString();
+        }
+    };
+
     public static class BeanForHeaderDelegateProviderTest {
         public static String getValue() {
             return "CORRECT_VALUE";
@@ -116,13 +141,18 @@
 
     @Test
     public void testTheProviderIsFound() {
+        int found = 0;
         for (HeaderDelegateProvider provider : ServiceFinder.find(HeaderDelegateProvider.class, true)) {
-            Assert.assertEquals(provider.getClass(), BeanHeaderDelegateProvider.class);
+            if (provider.getClass() == BeanHeaderDelegateProvider.class
+                    || provider.getClass() == EmptyContentTypeHandler.class) {
+                found++;
+            }
         }
+        Assert.assertEquals(2, found);
     }
 
     @Test
-    public void headerDelegateIsUsedWhenRuntimeDelegateDecoratorIsUsed() {
+    public void testHeaderDelegateIsUsedWhenRuntimeDelegateDecoratorIsUsed() {
         MultivaluedHashMap headers = new MultivaluedHashMap();
         headers.put(HEADER_NAME, Arrays.asList(new BeanForHeaderDelegateProviderTest()));
         MultivaluedMap<String, String> converted = HeaderUtils.asStringHeaders(headers, null);
@@ -134,7 +164,7 @@
     }
 
     @Test
-    public void headerDelegateIsNotUsed() {
+    public void testHeaderDelegateIsNotUsed() {
         MultivaluedHashMap headers = new MultivaluedHashMap();
         headers.put(HEADER_NAME, Arrays.asList(new BeanForHeaderDelegateProviderTest()));
 
@@ -147,6 +177,54 @@
         testMap(converted, DISABLED_VALUE);
     }
 
+    @Test
+    public void testGetMediaTypeInInboundMessageContext() {
+        ClientConfig config = new ClientConfig().property(CommonProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);
+        InboundMessageContext inboundMessageContext = new InboundMessageContext(config.getConfiguration()) {
+            @Override
+            protected Iterable<ReaderInterceptor> getReaderInterceptors() {
+                return null;
+            }
+        };
+        inboundMessageContext.header(HttpHeaders.CONTENT_TYPE, "");
+        try {
+            inboundMessageContext.getMediaType();
+            fail("Expected HeaderValueException has not been thrown");
+        } catch (HeaderValueException ex) {
+            // expected
+        }
+
+        config.property(CommonProperties.METAINF_SERVICES_LOOKUP_DISABLE, false);
+        inboundMessageContext = new InboundMessageContext(config.getConfiguration()) {
+            @Override
+            protected Iterable<ReaderInterceptor> getReaderInterceptors() {
+                return null;
+            }
+        };
+        inboundMessageContext.header(HttpHeaders.CONTENT_TYPE, "");
+        MediaType mediaType = inboundMessageContext.getMediaType();
+        Assert.assertEquals(MediaType.APPLICATION_OCTET_STREAM_TYPE, mediaType);
+    }
+
+    @Test
+    public void testGetMediaTypeInOutboundMessageContext() {
+        ClientConfig config = new ClientConfig().property(CommonProperties.METAINF_SERVICES_LOOKUP_DISABLE, true);
+        OutboundMessageContext outboundMessageContext = new OutboundMessageContext(config.getConfiguration());
+        outboundMessageContext.getHeaders().add(HttpHeaders.CONTENT_TYPE, "");
+        try {
+            outboundMessageContext.getMediaType();
+            fail("Expected HeaderValueException has not been thrown");
+        } catch (IllegalArgumentException ex) {
+            // expected
+        }
+
+        config.property(CommonProperties.METAINF_SERVICES_LOOKUP_DISABLE, false);
+        outboundMessageContext = new OutboundMessageContext(config.getConfiguration());
+        outboundMessageContext.getHeaders().add(HttpHeaders.CONTENT_TYPE, "");
+        MediaType mediaType = outboundMessageContext.getMediaType();
+        Assert.assertEquals(MediaType.APPLICATION_OCTET_STREAM_TYPE, mediaType);
+    }
+
     private void testMap(MultivaluedMap<String, String> map, String expectedValue) {
         for (Map.Entry<String, List<String>> entry : map.entrySet()) {
             Assert.assertEquals(HEADER_NAME, entry.getKey());
diff --git a/tests/e2e-entity/src/test/resources/META-INF/services/org.glassfish.jersey.spi.HeaderDelegateProvider b/tests/e2e-entity/src/test/resources/META-INF/services/org.glassfish.jersey.spi.HeaderDelegateProvider
index 1cca664..73f95b8 100644
--- a/tests/e2e-entity/src/test/resources/META-INF/services/org.glassfish.jersey.spi.HeaderDelegateProvider
+++ b/tests/e2e-entity/src/test/resources/META-INF/services/org.glassfish.jersey.spi.HeaderDelegateProvider
@@ -1 +1,2 @@
-org.glassfish.jersey.tests.e2e.header.HeaderDelegateProviderTest$BeanHeaderDelegateProvider
\ No newline at end of file
+org.glassfish.jersey.tests.e2e.header.HeaderDelegateProviderTest$BeanHeaderDelegateProvider
+org.glassfish.jersey.tests.e2e.header.HeaderDelegateProviderTest$EmptyContentTypeHandler
\ No newline at end of file