I am trying to consume a JAX-RS 2 Response with Jersey client 2.17.
Response response = ClientBuilder.newClient()
.target("http://localhost:8080/api")
.path("organisations")
.request(MediaType.APPLICATION_JSON)
.get(); //[1] .get(GetOrganisationsResponse.class);
//[2] System.out.println("headers=" + response.getHeaders());
//[3] String json = response.readEntity(String.class);
//[4] System.out.println("json=" + json);
Organisations orgs = response.readEntity(Organisations.class);
The response is served by Mule apikit.
<flow name="get:/organisations:my-api-config" >
<set-payload value="#[app.registry['organisations'].getOrganisations(message.inboundProperties['start'], message.inboundProperties['pages'])]" doc:name="Set Payload" />
</flow>
POJOs are generated from a RAML description. Here is the generated POJO for the above service:
/**
* A collection of organisations
*
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@Generated("org.jsonschema2pojo")
@JsonPropertyOrder({
"size",
"organisations"
})
public class Organisations {
/**
*
* (Required)
*
*/
@JsonProperty("size")
private Integer size;
@JsonProperty("organisations")
private List<Organisation> organisations = new ArrayList<Organisation>();
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
/**
*
* (Required)
*
* @return
* The size
*/
@JsonProperty("size")
public Integer getSize() {
return size;
}
/**
*
* (Required)
*
* @param size
* The size
*/
@JsonProperty("size")
public void setSize(Integer size) {
this.size = size;
}
public Organisations withSize(Integer size) {
this.size = size;
return this;
}
/**
*
* @return
* The organisations
*/
@JsonProperty("organisations")
public List<Organisation> getOrganisations() {
return organisations;
}
/**
*
* @param organisations
* The organisations
*/
@JsonProperty("organisations")
public void setOrganisations(List<Organisation> organisations) {
this.organisations = organisations;
}
public Organisations withOrganisations(List<Organisation> organisations) {
this.organisations = organisations;
return this;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
public Organisations withAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
return this;
}
}
... and here is the generated GetOrganisationsResponse
for the same service:
public class GetOrganisationsResponse
extends support.ResponseWrapper
{
private GetOrganisationsResponse(Response delegate) {
super(delegate);
}
/**
* OK
*
* @param entity
*
*/
public static Organisations.GetOrganisationsResponse withJsonOK(model.Organisations entity) {
Response.ResponseBuilder responseBuilder = Response.status(200).header("Content-Type", "application/json");
responseBuilder.entity(entity);
return new Organisations.GetOrganisationsResponse(responseBuilder.build());
}
}
In trying to GET an instance of Organisations
on the client side, I observed the following:
- All the properties from
Organisations
end up populated in the additionalProperties
member suggesting none were recognized by the parser,
- If I comment out the additionalProperties member and getter/setter, the parser's error is this:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "date" (class model.Organisations), not marked as ignorable (2 known properties: "size", "organisations"])
- ... this makes sense if the date, headers (and others) fields were not mapped to the Response, but to the Organisations instead but why?
- when uncommenting the lines marked 2, 3 and 4, the Response converted to String looks as follows. Shouldn't the
GetOrganisationsResponse
headers have been parsed and returned as a result of response.getHeaders()
?
headers={X-MULE_ENCODING=[UTF-8], Date=[Mon, 30 Mar 2015 02:16:57 +0000], Content-Length=[746], X-MULE_SESSION=[...], Connection=[close], http.status=[200], Content-Type=[application/json], Server=[Mule Core/3.6.1]} json={"date":null,"lastModified":null,"headers":{"Content-Type":["application/json"]},"entity":{"size":3,"organisations":[{"oid":"54df33936d725e370b000004","name":"org1","additionalProperties":{}},{"oid":"54df34406d725e370b000006","name":"org2","additionalProperties":{}},{"oid":"54df33c96d725e370b000005","name":"org3","additionalProperties":{}}],"additionalProperties":{}},"status":200,"mediaType":{"type":"application","subtype":"json","parameters":{},"wildcardType":false,"wildcardSubtype":false},"allowedMethods":[],"links":[],"cookies":{},"entityTag":null,"statusInfo":"OK","metadata":{"Content-Type":["application/json"]},"stringHeaders":{"Content-Type":["application/json"]},"length":-1,"language":null,"location":null}
- Finally when trying to force the Response as generated on the server side with
.get(GetOrganisationsResponse.class)
(uncommenting 1), the outcome is:
Can not find a deserializer for non-concrete Map type [map type; class javax.ws.rs.core.MultivaluedMap, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]]
The culprit is the following code in the parent class of GetOrganisationResponse
called ResponseWrapper
:
@Override public MultivaluedMap getHeaders() { return delegate.getHeaders(); }
@Override public MultivaluedMap getStringHeaders() { return delegate.getStringHeaders(); }
Thanks for pointing me in the right direction:
- Is
client.get()
or client.get(GetOrganisationsResponse.class)
the recommended aproach to parse a Mule Apikit response on the client? (I could not find answers in Mule docs)
- Considering I get errors for either option: Thanks for your help&ideas on how to fix them.