jax-rsresteasy

JAX-RS: Several different ContextResolver<ObjectMapper>?


I have a backend server running on JAX-RS (RestEasy flavour).

Some of the endpoint responses contain dates, which I want to be formatted as ISO strings.

@Value // Lombok annotation
public class MyResponse {
   String someDateAsString;
}

Because I don't want the conversion to be "improvised" by each developer in each Response, I standardize it like this :

@Value
public class MyResponse {
   LocalDateTime someDate;
}

And I make the conversion happen automatically by using the corresponding setting of Jackson :

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JacksonDatatypeProducer implements ContextResolver<ObjectMapper> {
    private final ObjectMapper mObjectMapper;

    public JacksonDatatypeProducer() {
        this.mObjectMapper = new ObjectMapper()
            .findAndRegisterModules()
            .configure(WRITE_DATES_AS_TIMESTAMPS, false); // <-- where the magic happens

    }

    @Override
    public ObjectMapper getContext(final Class<?> objectType) {
        return mObjectMapper;
    }
}

This works great, I get my dates as ISO strings in the frontend.

PROBLEM:

My backend is also the consumer of many third-party services. They are called through various instances of javax.ws.rs.client.Client. The framework knows how to inject them automatically, because this kind of class is present:

public class HttpClientProducer {
    private final ResteasyClient mClient;

    @Inject
    HttpClientProducer(final Logger logger) {
        mClient = ((ResteasyClientBuilder) ClientBuilder.newBuilder().register(new JacksonDatatypeProducer()))
            .build();
    }

    @Produces
    @ApplicationScoped
    public Client getClient() {
        return mClient;
    }
}

I want my backend (as a server) to have its own configuration for Jackson. I do not want it to share the same settings as all those 3rd party clients (especially not the WRITE_DATES_AS_TIMESTAMPS setting).

I cannot wrap my head around the proper way of telling the server (and only the server) explicitly which Jackson settings to use. I.e. there's too much magic wiring, I don't know where to act in the instantiation flow -- whether be explicit or implicit locations.

I've been looking for a class similar to this HttpClientProducer, but meant to instantiate the backend as a server instead. There doesn't seem to be one.

MY ATTEMPTS:

First of all I wonder why the JacksonDatatypeProducer is instantiated manually in HttpClientProducer. Is that a "mistake" in the legacy code? I.e. why wouldn't they use dependency injection if it's to instantiate the same JacksonDatatypeProducer anyways?


Solution

  • You should be able to use the jakarta.ws.rs.ConstrainedTo annotation (or javax.ws.rs.ConstrainedTo if you're using older versions of Jakarta REST and RESTEasy).

    You'd annotate your ContextResolver for the client with @ConstrainedTo(RuntimeType.CLIENT) and your server with @ConstrainedTo(RuntimeType.SERVER).