All Downloads are FREE. Search and download functionalities are using the official Maven repository.

au.net.causal.shoelaces.jersey.common.JacksonJsonStringReaderProvider Maven / Gradle / Ivy

The newest version!
package au.net.causal.shoelaces.jersey.common;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.ext.MessageBodyReader;
import jakarta.ws.rs.ext.Providers;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.base.ProviderBase;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

@Consumes(MediaType.APPLICATION_JSON)
public class JacksonJsonStringReaderProvider implements MessageBodyReader
{
    @Context
    protected Providers workers;

    /**
     * Finds an already-registered delegate for handling the specified media type for arbitrary objects.
     * This should typically find the Jackson provider, however if another has been registered then this will
     * be picked up.
     *
     * @param annotations an array of the annotations attached to the message entity instance.
     * @param mediaType the media type of the HTTP entity.  This will usually be {@link MediaType#APPLICATION_JSON_TYPE} since this is what this class is annotated
     *                  to handle, but subclasses may change this.
     *
     * @return a delegate writer for the given media type.
     *
     * @throws RuntimeException if no writer could be found for the given media type.  This provider should only be used when there is a known provider for handled
     *                          media type already registered, such as Jackson for application/json.
     */
    protected MessageBodyReader delegate(Annotation[] annotations, MediaType mediaType)
    {
        MessageBodyReader delegate = workers.getMessageBodyReader(Object.class, Object.class, annotations, mediaType);

        if (delegate == null)
            throw new RuntimeException("Expected to find a MessageBodyReader implementation to handle " + mediaType + " - Object types but none were found.");

        //For Jackson readers, to ensure they actually handle strings remove String as an 'untouchable' type
        //Otherwise they'll just ignore Strings since by default they are blacklisted
        if (delegate instanceof ProviderBase)
            ((ProviderBase)delegate).removeUntouchable(String.class);

        return delegate;
    }

    @Override
    public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType)
    {
        return delegate(annotations, mediaType).isReadable(type, genericType, annotations, mediaType);
    }

    @Override
    public String readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType,
                           MultivaluedMap httpHeaders, InputStream entityStream)
    throws IOException, WebApplicationException
    {
        Object result = delegate(annotations, mediaType)
                            .readFrom(Object.class, Object.class, annotations, mediaType, httpHeaders, entityStream);

        //For string and primitive JSON types just use toString()
        //Technically we could reject non-strings but let's be a bit lenient
        if (result instanceof CharSequence || result instanceof Boolean || result instanceof Number)
            return result.toString();
        //Normally shouldn't happen but let's be resilient in this case
        else if (result == null)
            return null;

        //Tried to use a String type for an uncovertable JSON, throw an error
        //If this provider wasn't registered Jersey would just return string containing entire response
        //but for a bad JSON type it is better to indicate the mistake
        //Client should really adjust their type in this case
        throw new UnexpectedJsonTypeException(result, "Cannot convert JSON reader result into string - got " + result + " (" + result.getClass() + ")");
    }
}