CXF Rest Client - set ResponseReader for POJO (JAXBElement) - java

I want to transfer a POJO Object with my CXF Rest Client. It already works for JAXB annotated objects. So I had a lot attempts for it.
I tried
reader.setEntityClass(ObjectPOJO.class);
and something like
reader.setEntityClass(JAXBElement<ObjectPOJO>.class);
Both doesnt work. For the second attempt the code is wrong. I dindt get it to set the entity class to jaxbelement. Maybe it works with it.
After sending the object with:
Response response = client.path(PATH).post(new JAXBElement<ObjectPOJO>(new QName("pojo"), ObjectPOJO.class, pojoObject));
i tried to get my entity with different attempts. Something like:
ObjectPOJO pojo = ((JAXBElement<TenantPOJO>) res.getEntity()).getValue();
Does someone know if I have to register the ObjectPOJO.class or the JAXBElement.class. If second attempt is right, how does it look right in code?
Whats the right code to get the entity out of the response?
My Service looks like this:
#POST
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response postPojo(JAXBElement<ObjectPOJO> pojo);
Edit:
Error
ERROR org.apache.cxf.jaxrs.client.AbstractClient - .Problem with reading the response message, class : class javax.ws.rs.core.Response, ContentType : application/xml.

To transfer objects (PoJos) between rest clients and rest services, there is no need to transfer it using the explicit JAXB conversion. The Jersey container is responsible for the necessary conversion provided the transfer objects are JAXB Annotated Objects.
Response response = client.path(PATH).post(postObject);
#XmlRootElement
public class PostObject
{
////
}

Related

How to register an existing Jersey MessageBodyReader Provider as handling additional Content Types?

I have a REST webservice that handles JSON requests. I want to implement against the SCIM spec, to allow an Azure AD integration to provision users within my application.
I've written the method:
#POST
#Path("/scim/Users")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
ScimUser createUser(#HeaderParam("Authorization") String authHeader,
ScimUser user);
However, AAD is making requests with Content-Type: application/scim+json, so my application is returning a 415: Media Type Not Supported error.
If I just swap over to
#Consumes({MediaType.APPLICATION_JSON, "application/scim+json"})
My application routes the request to the correct method, but still throws a 415 error, this time because it can't find an appropriate message body reader.
I have a MessageBodyReader that can handle the incoming messages; "scim+json" is fully parsable as JSON. However, I can't modify the library code to add a new #Consumes annotation onto the existing class.
Is there a way to let Jersey know that this class can handle these messages?
My current workaround is to create a new class:
#Provider
#Consumes(ScimService.SCIM_JSON_TYPE)
#Produces(ScimService.SCIM_JSON_TYPE)
public class ScimJsonProvider extends ExistingJsonProvider {
public ScimJsonProvider() {
super();
}
}
and register that, but this feels kind of hacky, and I'd like to know if there's a better way.
Edit: this also doesn't seem to work; it seems like this ScimJsonProvider is sometimes being selected as the MessageBodyWriter for application/json events, and then later is throwing a 415 because that's not a type registered in its #Produces list. :(

GetEntity vs ReadEntity in Response (Javax.ws.rs)

I am writing a client to consume a RestService and I have to read an entity out of the response and I am totally confused which method out of the two (getEntity vs readEntity) should be used.
I have to retrieve the entity whenever I get a WebApplicationException.
So, my code more or less looks like.
catch(WebApplicationException ex) {
// Do something with ex.getResponse
}
From, whatever I have tested,
ex.getResponse().hasEntity() ---> true
ex.getResponse().getEntity() == null ---> true
I don't know how it is working but if the first is true then how second statement could be true.
Surprisingly, readEntity worked fine for me and I was able to read the
entity out from the response.
But, after reading the entity through readEntity,
this call gives false.
ex.getResponse().getEntity() == null --> false
Can someone help me understand what is really happening behind the scenes?
The Response class has two uses: server side and client side. On the server side, it's called the outbound response. On the client, it's inbound response.
Outbound
#GET
public Response get() {
MyModel model = new MyModel();
return Response.ok(model).build();
}
Inbound
Response response = ClientBuilder.newClient().target(url).request().get();
The getEntity() method is meant to be used on the server side because you want to get the entity object. There's not much use for it for us, but Jersey will use it to get the entity object to serialize it before it sends it out.
The readEntity() method is to be used on the client side because you are trying to read the entity stream. If you try to call this on the server side, you will get an error saying that you can't read the stream on an outbound response.
As far as the behavior you're experiencing, I can't really explain why they implemented like this.
This behaviour is documented in the API:
public abstract <T> T readEntity(Class<T> entityType)
Read the message entity input stream as an instance of specified Java type using a MessageBodyReader that supports mapping the message entity stream onto the requested type.
[...]
A message instance returned from this method will be cached for subsequent retrievals via getEntity().
The first call to ex.getResponse().getEntity() is null, because the Entity hasn't been read yet. After calling readEntity(), the parsed entity will be returned as parsed by getEntity().

Can I deserialize a JAX-RS Response?

I've written a JAX-RS API that looks like:
#POST
#Path("findallbyname")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.TEXT_PLAIN)
Response findAllByName(String payload);
Where Response is javax.ws.rs.core.Response. The entity property of the Response is a List<String>, so this response just returns the JSON serialization of a list of strings.
In a separate app, I'm calling the API like so (using an http-remoting library found here):
Service service = JaxRsClient.builder().build(Service.class, "<user-agent>", "<uri>");
Response response = service.findAllByName("some string");
However, I get this error:
Caused by: com.fasterxml.jackson.databind.JsonMappingException:
Can not construct instance of javax.ws.rs.core.Response:
abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: java.io.BufferedReader#2ef14fe; line: 1, column: 1]
Ok, this seems like a straightforward object serde problem -- it looks like since javax.ws.rs.core.Response is an abstract type, it can't deserialize on the client side (the request goes through and comes back with status 200, according to the server request logs).
My question is, since my original API was written to respond with the abstract Response object type, am I out of luck when it comes to deserializing the response object I get on the client side? I can change the API, but I wanted to ask and see if there was a better way to solve this deserialization problem on the client side first.

A message body writer for Java type, class myPackage.Sample, and MIME media type, application/xml, was not found

I am using Jersey RESTful webservices. I wrote client as below but it throws above exception which i mentioned in the title.
public class MyRestClient {
public static void main(String[] args) {
Client client = Client.create();
WebResource resource = client.resource("http://localhost:8080/myapp/rest/a/update/123");
Sample b1 = new Sample("debris");
ClientResponse response = resource.type(MediaType.APPLICATION_XML).put(ClientResponse.class, b1);
}
}
Someone told to annotate Sample class with #XmlRootElement. But i cannot do it since Sample is generated by third party. Any help ?
This is a little difficult to answer without seeing your REST service class, but I'm guessing you're trying to consume your data as a Sample object in your service, as so:
#PUT
public Response updateSample(Sample sample) {
...
But this relies on Jersey being able to automatically marshall your XML data into a Sample object, which would require the JAXB annotations on the Sample class, as you pointed out, and since those are missing you are getting the error you describe.
instead, you can consume it as a String in your service, like so:
#PUT
public Response updateSample(String sampleStr) {
...
But now you're responsible for parsing your sampleStr data as xml and converting it into a Sample object (which is not necessarily a bad thing). But, since the Sample class is not annotated for XML, Jersey won't even be able to convert it into XML for your client to send.
See this article for more information on different ways to transfer data back and forth with Jersey REST services: http://usna86-techbits.blogspot.com/2013/08/restful-java-web-service-marshalling.html
You might have an easier time processing it manually on the server if you pass your data as JSON. Look at the JUnit test class towards the bottom of that article for ideas on how to do that.
Please include your service class if you need more assistance.

Restful service endpoint to produce XML response for ResponseEntity< HashMap<String, Serializable>>

I am trying to implement a RESTful service endpoint which produces XML responses. The return entity for this service call is a HashMap which has the data for the output to be generated. But I keep getting the following exception while invoking the service:
HttpMediaTypeNotAcceptableException: Could not find acceptable
representation
To investigate the issue, I wrote another endpoint which produces a response for a single object (say, Employee). I have annotated this class with #XmlRootElement and invoking it works just fine. If I remove the #XmlRootElement annotation from the Employee class, this endpoint will also fail and give the same exception which I mentioned above.
As per my understanding the root object in the ResponseEntity should be annotated with #XmlRootElement. My problem centers around how to use this annotation on collections like Map, List etc..
All help appreciated, thanks.
I don't think this is possible -- you will most likely have to create some sort of wrapper or DTO around your Collection/Map. This: Using JAXB to unmarshal/marshal a List<String> I believe is related to your use-case.

Categories

Resources