I'm trying to set up a Kotlin Spring Boot application that uses Jersey to make REST calls to DropWizard API applications from the Services/Repository layer. In the API layer the controllers use Spring MVC classes and annotations. So i've got Jersey set up as a filter that passes along 404's to Spring MVC which results in an application that is able to handle both type of requests.
I have abstracted an ApiUtils class where the JAX-RS calls are constructed, executed and the response is handled. This ApiUtils class is an @Component and has an @Autowired constructor to initialize it.
public ApiUtils(Client jerseyClient, ObjectMapper mapper, ServiceNameProvider serviceNameProvider) {
this.client = jerseyClient;
// this.client = ClientBuilder.newClient();
this.mapper = mapper;
this.serviceName = serviceNameProvider.getServiceName();
//in case a jersey client is inserted with a ClientBuilder.newClient() no object mapper
//is registered; to avoid fall back to default register the given mapper
if (this.client != null
&& this.client.getConfiguration() != null
&& !this.client.getConfiguration().isRegistered(JacksonJsonProvider.class)
&& !this.client.getConfiguration().isRegistered(JacksonFeature.class)
) {
this.client.register(new JacksonJsonProvider(this.mapper));
}
}
Now i discovered that even though the primary custom objectmapper configured in Spring Boot is autowired into this ApiUtils class, the custom objectmapper configuration is not being applied. Even when i create a new client and register the objectmapper i experience the same result. I printed all settings of the custom objectmapper in the APIUtils class and they are exactly as configured.
When i add Annotations to the class that needs to be deserialized it succeeds, but without the annotation using the custom objectmapper it fails.
A good example is the DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES it is set to false but deserialization fails when there are unkown properties. As soon as i add the @JsonIgnoreProperties(ignoreUnknown = true) to the class itself it succeeds.
I've seen the custom objectmapper get registered in the autoconfiguration class of Jersey and Jackson when starting the application, i've seen the custom objectmapper being autowired in my ApiUtils class. But the errors that i get clearly indicate that a default objectmapper without clustomization is used.
This is my current config in Spring Boot to Customize the Objectmapper. But i have tried all builders and other types of configs i could find searching for the correct setup.
MyCustomObjectMapper
@Configuration
@Primary
class MyCustomObjectMapper : ObjectMapper() {
init {
val kotlinModule: KotlinModule = KotlinModule.Builder()
.configure(KotlinFeature.NullIsSameAsDefault, true)
.configure(KotlinFeature.StrictNullChecks, true)
.configure(KotlinFeature.NullToEmptyCollection, true)
.configure(KotlinFeature.NullToEmptyMap, true)
.configure(KotlinFeature.SingletonSupport, true)
.build()
this.registerModules(
kotlinModule,
JavaTimeModule(),
Jdk8Module(),
)
...
// deserialization configuration
this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
...
}
}
JacksonConfig
@Provider
class JacksonConfig : ContextResolver<ObjectMapper> {
@Autowired
private lateinit var objectMapper: MyCustomObjectMapper
override fun getContext(type: Class<*>?): ObjectMapper {
return objectMapper
}
}
JerseyConfig
@Configuration
@ApplicationPath(JERSEY_API_PATH)
class JerseyConfig : ResourceConfig() {
init {
register(packages("eu.mypackage.services.web.cg.configuration"));
register(packages("eu.mypackage.services.web.cg.controller"));
register(EndpointJerseyLoggingListener(JERSEY_API_PATH));
//https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/howto-jersey.htmls
properties = mapOf(
ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR to true,
ServletProperties.FILTER_FORWARD_ON_404 to true
)
}
}
I'm quite out of options... Is Spring MVC causing this issue, although i do not use it in the ApiUtils class? Is there another way to use a customized objectmapper for the Jersey Client and then autowire it to a external java ApiUtils class?
The issue seemed to be that the Jersey client that was configured in my project was not correctly setup. This in combination with scanning of
register(packages("eu.mypackage.services.web.cg.configuration"));that contained theSeemed to cause my ApiUtils class to autowire a Jersey client that did not use the customized objectmapper.
So by annotating the custom objectmapper class with
removing the
completely and creating a JerseyClientConfiguration class:
I finally got the JerseyClient to use the custom configuration.
Maybe there is a better Spring Boot way to get the same config working. If so feel free to enlighten us.