Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support GSON media #5090

Merged
merged 4 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@
<artifactId>jersey-media-json-processing</artifactId>
<version>${project.version}</version>
</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>
Expand Down
81 changes: 81 additions & 0 deletions media/json-gson/pom.xml
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>
* &#64;Provider
* &#64;class GsonContextResolver implements ContextResolver&lt;Gson&gt; {
* &#64;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;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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 *&#47;json and
* *&#47;*+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();
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.glassfish.jersey.gson.internal.JsonGsonAutoDiscoverable
Original file line number Diff line number Diff line change
@@ -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.
Loading