I had to upgrade several packages to pass a whitesource security scan and now that the dependencies are upgraded the custom HttpMessageConverter
that was intercepting and building the response previously is no longer working. The relevant dependency upgrades are shown below.
Tomcat-embed-core 8.5.50 -> 9.0.30
Spring cloud contract release 2.0.1.RELEASE-> 2.0.6.RELEASE
Spring boot version 2.0.4.RELEASE -> 2.0.6.RELEASE
Jackson-databind 2.9.6 -> 2.10.0.pr1
Jackson-core: 2.10.1 -> 2.10.0.pr1
Here is the custom HttpMessageConverter that was working previously:
private class JsonApiHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
JsonApiHttpMessageConverter() {
super(MediaType.valueOf(ResponseType.MEDIA_TYPE_JSON_API));
}
@Override
protected boolean supports(final Class<?> clazz) {
return clazz == HttpErrorResponse.class;
}
@Override
protected Object readInternal(final Class<?> clazz, final HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
protected void writeInternal(final Object o, final HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
try (OutputStreamWriter outputStream =
new OutputStreamWriter(outputMessage.getBody(), Charset.defaultCharset())) {
JsonAPIDocument document;
if (o instanceof HttpErrorResponse) {
// Build Error Document
final HttpErrorResponse errorResponse = (HttpErrorResponse) o;
final JsonApiErrorDTO errorDTO = new JsonApiErrorDTO(Integer.toString(errorResponse.getStatus()),
Integer.toString(errorResponse.getCode()), errorResponse.getMessage());
document = new JsonAPIDocument();
document.addError(errorDTO);
} else { // Build JSON API Response Document
final JsonApiDocumentBuilder documentBuilder = new JsonApiDocumentBuilder();
documentBuilder.data(o);
document = documentBuilder.build();
}
outputStream.write(new Gson().toJson(document));
outputStream.flush();
} catch (final InvalidJsonApiObjectException ijaoe) {
LOG.error("Error in converting Object to JsonAPIDocument", ijaoe);
throw new ServiceException(ijaoe);
}
}
}
it is defined in a class that registers the message converters like below
@Configuration
public class ServiceConfiguration implements WebMvcConfigurer {
...
@Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
converters.add(new JsonApiHttpMessageConverter());
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new StringHttpMessageConverter());
converters.add(new ResourceHttpMessageConverter());
converters.add(new SourceHttpMessageConverter<>());
converters.add(new AllEncompassingFormHttpMessageConverter());
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new MappingJackson2CborHttpMessageConverter());
converters.add(new Jaxb2RootElementHttpMessageConverter());
}
@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.valueOf(ResponseType.MEDIA_TYPE_JSON_API));
}
}
this is the error response code:
@XmlRootElement(name = "error")
@XmlAccessorType(XmlAccessType.FIELD)
@JsonApiType(type = "error")
public class HttpErrorResponse implements Serializable {
private static final long serialVersionUID = 1321088631120274988L;
@XmlTransient
private int status;
@XmlElement(name = "code", required = true)
private int code;
@XmlElement(name = "message", required = true)
@JsonApiAttribute(name = "detail")
private String message;
@JsonApiIgnore
@XmlElement(name = "uuid", required = true)
private String uuid;
public HttpErrorResponse() {
this.uuid = UUID.randomUUID().toString();
}
public HttpErrorResponse(final int status, final int code, final String message) {
this();
this.status = status;
this.code = code;
this.message = message;
}
public int getStatus() {
return status;
}
public void setStatus(final int status) {
this.status = status;
}
public int getCode() {
return code;
}
public void setCode(final int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(final String message) {
this.message = message;
}
public String getUuid() {
return uuid;
}
public void setUuid(final String uuid) {
this.uuid = uuid;
}
}
and the object does appear to be the one being returned but the message converter is not converting it. The old response format with the converter working is:
"{\"included\":[],\"errors\":[{\"status\":\"400\",\"code\":\"990002\",\"detail\":\"Invalid subscription id: XYZ\"}]}",
and the current response is:
"responseBody": "{\"status\":400,\"code\":990002,\"message\":\"Invalid subscription id: XYZ\",\"uuid\":\"018fe1e3-3936-4c53-8612-61ef778fd811\"}",
I'm sorry if my question is unclear please let me know if there is anything I'm leaving out. I'm confused as to why the converter is no longer intercepting the response and updaing the format. Also here is the format that the converter should be building:
@JsonPropertyOrder({ "data", "included", "errors" })
@JsonInclude(JsonInclude.Include.NON_NULL)
public class JsonAPIDocument extends AbstractJsonAPIDocument {
@JsonInclude(JsonInclude.Include.NON_NULL)
private IJsonAPIDTO data;
@JsonInclude(JsonInclude.Include.NON_NULL)
public IJsonAPIDTO getData() {
return data;
}
public void setData(IJsonAPIDTO data) {
this.data = data;
}
}
Here are the response headers:
"responseHeaders": {
"X-Origin-Ref": "microservice",
"X-Transaction-Ref": "b2e3d807-0d6f-40e7-90ad-43d5ecb455c2",
"Content-Type": "application/vnd.api+json"
}
and ResponseType.MEDIA_TYPE_JSON_API is
public static final String MEDIA_TYPE_JSON_API = "application/vnd.api+json"
As a desperate attempt I changed the support method of the JsonApiHttpMessageConverter class to always return true and the converter is still not picking up the response