Support GSON media (#5090)
* Support GSON media
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
diff --git a/bom/pom.xml b/bom/pom.xml
index 23dbb16..0cd67c4 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -298,6 +298,11 @@
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-json-gson</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/media/json-gson/pom.xml b/media/json-gson/pom.xml
new file mode 100644
index 0000000..d1b45ea
--- /dev/null
+++ b/media/json-gson/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright (c) 2022 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
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>project</artifactId>
+ <version>2.37-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>jersey-media-json-gson</artifactId>
+ <packaging>jar</packaging>
+ <name>jersey-media-json-gson</name>
+
+ <description>
+ Jersey GSON support module.
+ </description>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.sun.istack</groupId>
+ <artifactId>istack-commons-maven-plugin</artifactId>
+ <inherited>true</inherited>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <inherited>true</inherited>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <inherited>true</inherited>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Package>org.glassfish.jersey.gson.*</Export-Package>
+ <Import-Package>${javax.annotation.osgi.version},*</Import-Package>
+ </instructions>
+ <unpackBundle>true</unpackBundle>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.glassfish.jersey.core</groupId>
+ <artifactId>jersey-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/media/json-gson/src/main/java/org/glassfish/jersey/gson/JsonGsonFeature.java b/media/json-gson/src/main/java/org/glassfish/jersey/gson/JsonGsonFeature.java
new file mode 100644
index 0000000..9e5cc55
--- /dev/null
+++ b/media/json-gson/src/main/java/org/glassfish/jersey/gson/JsonGsonFeature.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2022 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.gson;
+
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+
+import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.gson.internal.JsonGsonAutoDiscoverable;
+import org.glassfish.jersey.gson.internal.JsonGsonProvider;
+import org.glassfish.jersey.internal.InternalProperties;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+/**
+ * Feature used to register Gson providers.
+ * <p>
+ * The Feature is automatically enabled when {@link JsonGsonAutoDiscoverable} is on classpath.
+ * Default GSON configuration obtained by calling {@code GsonBuilder.create()} is used.
+ * <p>
+ * Custom configuration, if required, can be achieved by implementing custom {@link javax.ws.rs.ext.ContextResolver} and
+ * registering it as a provider into JAX-RS runtime:
+ * <pre>
+ * @Provider
+ * @class GsonContextResolver implements ContextResolver<Gson> {
+ * @Override
+ * public Gson getContext(Class<?> type) {
+ * GsonBuilder builder = new GsonBuilder();
+ * // add custom configuration
+ * return builder.create();
+ * }
+ * }
+ * </pre>
+ *
+ */
+public class JsonGsonFeature implements Feature {
+
+ private static final String JSON_FEATURE = JsonGsonFeature.class.getSimpleName();
+
+ @Override
+ public boolean configure(final FeatureContext context) {
+ final Configuration config = context.getConfiguration();
+
+ final String jsonFeature = CommonProperties.getValue(
+ config.getProperties(),
+ config.getRuntimeType(),
+ InternalProperties.JSON_FEATURE, JSON_FEATURE, String.class);
+
+ // Other JSON providers registered.
+ if (!JSON_FEATURE.equalsIgnoreCase(jsonFeature)) {
+ return false;
+ }
+
+ // Disable other JSON providers.
+ context.property(PropertiesHelper.getPropertyNameForRuntime(
+ InternalProperties.JSON_FEATURE, config.getRuntimeType()), JSON_FEATURE);
+
+ context.register(JsonGsonProvider.class);
+
+ return true;
+ }
+}
diff --git a/media/json-gson/src/main/java/org/glassfish/jersey/gson/internal/JsonGsonAutoDiscoverable.java b/media/json-gson/src/main/java/org/glassfish/jersey/gson/internal/JsonGsonAutoDiscoverable.java
new file mode 100644
index 0000000..b9fa6e2
--- /dev/null
+++ b/media/json-gson/src/main/java/org/glassfish/jersey/gson/internal/JsonGsonAutoDiscoverable.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2022 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.gson.internal;
+
+import javax.annotation.Priority;
+import javax.ws.rs.core.FeatureContext;
+
+import org.glassfish.jersey.gson.JsonGsonFeature;
+import org.glassfish.jersey.internal.spi.AutoDiscoverable;
+import org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable;
+
+/**
+ * {@link ForcedAutoDiscoverable} registering {@link JsonGsonFeature} if the feature is not already registered.
+ * <p>
+ *
+ * @see JsonGsonFeature
+ */
+@Priority(AutoDiscoverable.DEFAULT_PRIORITY)
+public class JsonGsonAutoDiscoverable implements ForcedAutoDiscoverable {
+
+ @Override
+ public void configure(FeatureContext context) {
+ if (!context.getConfiguration().isRegistered(JsonGsonFeature.class)) {
+ context.register(JsonGsonFeature.class);
+ }
+ }
+}
diff --git a/media/json-gson/src/main/java/org/glassfish/jersey/gson/internal/JsonGsonProvider.java b/media/json-gson/src/main/java/org/glassfish/jersey/gson/internal/JsonGsonProvider.java
new file mode 100644
index 0000000..3799af1
--- /dev/null
+++ b/media/json-gson/src/main/java/org/glassfish/jersey/gson/internal/JsonGsonProvider.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2022 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.gson.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NoContentException;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+import javax.ws.rs.ext.Providers;
+
+import org.glassfish.jersey.gson.LocalizationMessages;
+import org.glassfish.jersey.message.internal.AbstractMessageReaderWriterProvider;
+import org.glassfish.jersey.message.internal.EntityInputStream;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * Entity provider (reader and writer) for Gson.
+ *
+ */
+@Provider
+@Produces({"application/json", "text/json", "*/*"})
+@Consumes({"application/json", "text/json", "*/*"})
+public class JsonGsonProvider extends AbstractMessageReaderWriterProvider<Object> {
+
+ private static final String JSON = "json";
+ private static final String PLUS_JSON = "+json";
+
+ private Providers providers;
+
+ public JsonGsonProvider(@Context Providers providers) {
+ this.providers = providers;
+ }
+
+ @Override
+ public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ return supportsMediaType(mediaType);
+ }
+
+ @Override
+ public Object readFrom(Class<Object> type, Type genericType,
+ Annotation[] annotations,
+ MediaType mediaType,
+ MultivaluedMap<String, String> httpHeaders,
+ InputStream entityStream) throws IOException, WebApplicationException {
+ EntityInputStream entityInputStream = new EntityInputStream(entityStream);
+ entityStream = entityInputStream;
+ if (entityInputStream.isEmpty()) {
+ throw new NoContentException(LocalizationMessages.ERROR_GSON_EMPTYSTREAM());
+ }
+ Gson gson = getGson(type);
+ try {
+ return gson.fromJson(new InputStreamReader(entityInputStream,
+ AbstractMessageReaderWriterProvider.getCharset(mediaType)), genericType);
+ } catch (Exception e) {
+ throw new ProcessingException(LocalizationMessages.ERROR_GSON_DESERIALIZATION(), e);
+ }
+ }
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ return supportsMediaType(mediaType);
+ }
+
+ @Override
+ public void writeTo(Object o, Class<?> type, Type genericType,
+ Annotation[] annotations,
+ MediaType mediaType,
+ MultivaluedMap<String, Object> httpHeaders,
+ OutputStream entityStream) throws IOException, WebApplicationException {
+ Gson gson = getGson(type);
+ try {
+ entityStream.write(gson.toJson(o).getBytes(AbstractMessageReaderWriterProvider.getCharset(mediaType)));
+ entityStream.flush();
+ } catch (Exception e) {
+ throw new ProcessingException(LocalizationMessages.ERROR_GSON_SERIALIZATION(), e);
+ }
+ }
+
+ private Gson getGson(Class<?> type) {
+ final ContextResolver<Gson> contextResolver = providers.getContextResolver(Gson.class, MediaType.APPLICATION_JSON_TYPE);
+ if (contextResolver != null) {
+ return contextResolver.getContext(type);
+ } else {
+ return GsonSingleton.INSTANCE.getInstance();
+ }
+ }
+
+ /**
+ * @return true for all media types of the pattern */json and
+ * */*+json.
+ */
+ private static boolean supportsMediaType(final MediaType mediaType) {
+ return mediaType.getSubtype().equals(JSON) || mediaType.getSubtype().endsWith(PLUS_JSON);
+ }
+
+ private enum GsonSingleton {
+ INSTANCE;
+
+ // Thread-safe
+ private Gson gsonInstance;
+
+ Gson getInstance() {
+ return gsonInstance;
+ }
+
+ GsonSingleton() {
+ this.gsonInstance = new GsonBuilder().create();
+ }
+ }
+}
diff --git a/media/json-gson/src/main/java/org/glassfish/jersey/gson/package-info.java b/media/json-gson/src/main/java/org/glassfish/jersey/gson/package-info.java
new file mode 100644
index 0000000..12e6b90
--- /dev/null
+++ b/media/json-gson/src/main/java/org/glassfish/jersey/gson/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2022 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 marshalling and unmarshalling using GSON.
+ */
+package org.glassfish.jersey.gson;
diff --git a/media/json-gson/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable b/media/json-gson/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable
new file mode 100644
index 0000000..7f4e0ee
--- /dev/null
+++ b/media/json-gson/src/main/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable
@@ -0,0 +1 @@
+org.glassfish.jersey.gson.internal.JsonGsonAutoDiscoverable
diff --git a/media/json-gson/src/main/resources/org/glassfish/jersey/gson/localization.properties b/media/json-gson/src/main/resources/org/glassfish/jersey/gson/localization.properties
new file mode 100644
index 0000000..b2ea26a
--- /dev/null
+++ b/media/json-gson/src/main/resources/org/glassfish/jersey/gson/localization.properties
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2022 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.gson.serialization=Error writing GSON serialized object.
+error.gson.deserialization=Error deserializing object from entity stream.
+error.gson.emptystream=GSON cannot parse empty input stream.
diff --git a/media/json-gson/src/test/java/org/glassfish/jersey/gson/internal/JsonGsonProviderTest.java b/media/json-gson/src/test/java/org/glassfish/jersey/gson/internal/JsonGsonProviderTest.java
new file mode 100644
index 0000000..c7884d6
--- /dev/null
+++ b/media/json-gson/src/test/java/org/glassfish/jersey/gson/internal/JsonGsonProviderTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2022 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.gson.internal;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.NoContentException;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Providers;
+
+import org.junit.Test;
+
+public class JsonGsonProviderTest {
+
+ @Test(expected = NoContentException.class)
+ public void shouldThrowNoContentException() throws IOException {
+ Providers providers = new EmptyProviders();
+ MessageBodyReader<Foo> mbr = (MessageBodyReader) new JsonGsonProvider(providers);
+ mbr.readFrom(Foo.class, Foo.class, new Annotation[0], APPLICATION_JSON_TYPE,
+ new MultivaluedHashMap<>(), new ByteArrayInputStream(new byte[0]));
+ }
+
+ private static final class Foo {
+ // no members
+ }
+
+ private static final class EmptyProviders implements Providers {
+
+ @Override
+ public final <T> MessageBodyReader<T> getMessageBodyReader(final Class<T> type, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return null;
+ }
+
+ @Override
+ public final <T> MessageBodyWriter<T> getMessageBodyWriter(final Class<T> type, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return null;
+ }
+
+ @Override
+ public final <T extends Throwable> ExceptionMapper<T> getExceptionMapper(final Class<T> type) {
+ return null;
+ }
+
+ @Override
+ public final <T> ContextResolver<T> getContextResolver(final Class<T> contextType, final MediaType mediaType) {
+ return null;
+ }
+
+ }
+
+}
diff --git a/media/pom.xml b/media/pom.xml
index 20fc3b4..4f0d77f 100644
--- a/media/pom.xml
+++ b/media/pom.xml
@@ -38,6 +38,7 @@
<modules>
<module>jaxb</module>
<module>json-binding</module>
+ <module>json-gson</module>
<module>json-jackson</module>
<module>json-jackson1</module>
<module>json-jettison</module>
diff --git a/pom.xml b/pom.xml
index 2466f40..4047aa1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2061,6 +2061,12 @@
<artifactId>yasson</artifactId>
<version>${yasson.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>${gson.version}</version>
+ </dependency>
<dependency>
<groupId>io.opentracing</groupId>
@@ -2205,5 +2211,6 @@
<servlet3.version>3.0.1</servlet3.version>
<servlet4.version>4.0.4</servlet4.version>
<yasson.version>1.0.11</yasson.version>
+ <gson.version>2.9.0</gson.version>
</properties>
</project>
diff --git a/tests/e2e-server/pom.xml b/tests/e2e-server/pom.xml
index 6b8f3aa..2650a3d 100644
--- a/tests/e2e-server/pom.xml
+++ b/tests/e2e-server/pom.xml
@@ -101,6 +101,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-json-gson</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<scope>test</scope>
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GsonCustomTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GsonCustomTest.java
new file mode 100644
index 0000000..3d9f085
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GsonCustomTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2022 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.e2e.server;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Date;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.gson.JsonGsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class GsonCustomTest extends JerseyTest {
+
+ private static final Date date = new Date(0);
+
+ @Path("/test")
+ public static class Resource {
+
+ @GET
+ @Consumes("application/json")
+ public Date get() {
+ return date;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(Resource.class).register(JsonGsonFeature.class).register(GsonContextResolver.class);
+ }
+
+ @Test
+ public void get() {
+ Response response = target("/test").request().get();
+ assertEquals(200, response.getStatus());
+ String obj = response.readEntity(String.class);
+ assertEquals("\"1970\"", obj);
+ }
+
+ @Provider
+ public static class GsonContextResolver implements ContextResolver<Gson> {
+ @Override
+ public Gson getContext(Class<?> type) {
+ GsonBuilder builder = new GsonBuilder();
+ builder.setDateFormat("yyyy");
+ return builder.create();
+ }
+ }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GsonDefaultTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GsonDefaultTest.java
new file mode 100644
index 0000000..724a328
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/GsonDefaultTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2022 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.e2e.server;
+
+import static org.junit.Assert.assertEquals;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.gson.JsonGsonFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+public class GsonDefaultTest extends JerseyTest {
+
+ @Path("/test")
+ public static class Resource {
+
+ @POST
+ @Consumes("application/json")
+ @Produces("application/json")
+ public Obj post(Obj entity) {
+ entity.setValue("bar");
+ return entity;
+ }
+
+ @GET
+ @Consumes("application/json")
+ public Obj get() {
+ Obj entity = new Obj();
+ entity.setValue("get");
+ return entity;
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(Resource.class)
+ .register(JsonGsonFeature.class);
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(JsonGsonFeature.class);
+ }
+
+ @Test
+ public void get() {
+ Response response = target("/test").request().get();
+ assertEquals(200, response.getStatus());
+ Obj obj = response.readEntity(Obj.class);
+ assertEquals("get", obj.getValue());
+ }
+
+ @Test
+ public void post() {
+ Obj obj = new Obj();
+ obj.setValue("foo");
+ Response response = target("/test").request().post(Entity.json(obj));
+ assertEquals(200, response.getStatus());
+ obj = response.readEntity(Obj.class);
+ assertEquals("bar", obj.getValue());
+ }
+
+ public static class Obj {
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+ public void setValue(String value) {
+ this.value = value;
+ }
+ }
+}