JAX-RS NoMessageBodyWriterFoundFailure - java

the method
of my jax-rs application:
#GET
#Produces (MediaType.APPLICATION_JSON)
public List <Document> getDocumentList(#HeaderParam("Range") String headerRange) {
int [] range = getRangeFromHeader(headerRange);
return facade.listByRange(range);
}
working properly.
But If modifications to the:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getDocumentList(#HeaderParam("Range") String headerRange) {
int[] range = getRangeFromHeader(headerRange);
return Response.ok(
facade.listByRange(range))
.header("Content-Range", getContentRangeStr(range)).build();
}
I receive an error
...NoMessageBodyWriterFoundFailure: Could not find MessageBodyWriter for response
object of type: java.util.ArrayList of media type: application/json...
Server Jboss 7.1.1
Please tell me what's wrong.
PS.sorry for my bad English.

The snippet below should do the trick.
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getDocumentList(#HeaderParam("Range") String headerRange) {
int[] range = getRangeFromHeader(headerRange);
return Response.ok(
new GenericEntity<List<Document>>( (List<Document>)facade.listByRange(range))
)
.header("Content-Range", getContentRangeStr(range)).build();
}
The anonymous GenericEntity subclass is required to supply the correct type information (otherwise erased by the compiler) for the writer.
-- EDIT
The reason why your code worked using org.jboss.resteasy.resteasy-jackson-provider but not with org.jboss.resteasy.resteasy-jettison-provider resides on the fundamental difference between the two providers:
the former (jackson) relies on a JavaBean model, discovering the properties of the objects to serialize, and needs no type information
the latter (jettyson) relies on the JAXB annotations, so it needs the underlying type information, erased by the compiler.

You're missing a library as described here:
Here is the solution
This means that you are missing a JSON library in your classpath. Jackson is one I’m using so adding this to your pom.xml will help:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.10.1</version>
</dependency>

Related

Java method Overloading with REST - org.codehaus.jackson.map.JsonMappingException

I have a REST service that has a POST endpoint. This POST endpoint needs to receive an object (TravelRequisitionFormDTO) as part of its body:
#POST
#Path("/request")
#ApiOperation(value="Request a trip. Submit the trip request.")
#ApiResponses({
#ApiResponse(code=200, message="Success"),
#ApiResponse(code=404, message="Not Found")
})
#Produces({ MediaType.APPLICATION_JSON })
public Response getSubmitTrip(#HeaderParam("Authorization") String token, #ApiParam(required = true) TravelRequisitionFormDTO travelRequisitionFormDTO, #Context HttpServletRequest request) {
...
}
So when I call the endpoint, I get the following error:
<p><b>message</b> <u>org.codehaus.jackson.map.JsonMappingException: Conflicting setter definitions for property
"contactMethods": utility.dataobjects.ContactObject#setContactMethods(1 params) vs
utility.dataobjects.ContactObject#setContactMethods(1 params)</u></p>
<p><b>description</b> <u>The request sent by the client was syntactically incorrect
(org.codehaus.jackson.map.JsonMappingException: Conflicting setter definitions for property
"contactMethods": utility.dataobjects.ContactObject#setContactMethods(1 params) vs
utility.dataobjects.ContactObject#setContactMethods(1 params)).</u></p>
The reason for the error is because the TravelRequisitionFormDTO has a member variable (called ContactObject) that has two methods that are overloaded. So when it tries to convert the JSON body to JAVA, I guess it does not know which overloaded method to use. I think it sees it as ambiguous.
public void setContactMethods(ArrayList list)
and
public void setContactMethods(String[] list)
I don't want to change ContactObject if possible, because it is used in a number of other places.
Question
Is there any way I can resolve this? i.e. so that the JSON body can be converted successfuly into the Java object?
you can keep single property accepting List. and your Contractobject can consume both Array & List.
You could annotate one setter with Jackson #JsonSetter annotation:
#JsonSetter
public void setContactMethods(ArrayList list)
Make sure that you use right package. In your case it would be org.codehaus.jackson.annotate.JsonSetter like you can see in the error message. It might happen that you have also com.fasterxml.jackson.annotation.JsonSetter in the classpath so you have to be careful not to mix it.
Alternatively you can use #JsonProperty instead.

Jersey vs RestEasy - JAX-RS XML collection root element name change

I have the following Jax-RS end-point:
#XmlRootElement(name = "foobar")
public class Foobar {}
#GET
#Produces(MediaType.APPLICATION_XML)
public Object getFoobars() {
return new GenericEntity<List<FooBar>>(service.getFooBars());
}
Using Jersey 1.x, it used to return:
<foobars>
<foobar>...</foobar>
<foobar>...</foobar>
</foobars>
Now that I use RestEasy, it returns:
<collection>
<foobar>...</foobar>
<foobar>...</foobar>
</collection>
How can I control the root name of a returned GenericEntity<List<X>> in Jax-RS (using Rest-Easy)?
Please note that I also return Json format and I need to keep the API backward-compatible (for exemple the root element is an array in Json and should stay the same)
Found the solution myself after digging a bit in the RestEasy source code. You just have to add the #Wrapped(element="___") annotation to the method:
import org.jboss.resteasy.annotations.providers.jaxb.Wrapped;
#GET
#Produces(MediaType.APPLICATION_XML)
#Wrapped(element = "foobars")
public Object getFoobars() {
return new GenericEntity<List<FooBar>>(service.getFooBars());
}
Works correctly for XML and properly ignored for JSON.

How can I use an optional number of parameters in a Jersey REST method?

I'm new to Jersey. So, please pardon any mistake.
I'm trying to setup a simple REST ws.
There is a method name getConnectedMHubs that have one required parameter thingID and two optional parameters: time and delta.
Is it possible to use the same method name for the two type of calls, with and without the optional parameters?
I tried to specify two pathes but got a ModelValidationException, that says:
A resource model has ambiguous (sub-)resource method for HTTP method
GET and input mime-types as defined by"#Consumes" and "#Produces"
annotations at Java methods public ...
Code sample:
#Path("/api")
public class RendezvousWebService {
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("connectedmhubs/{mhubid}")
public String getConnectedThings(#PathParam("mhubid") String strMHubID) {
// ...
return "{}";
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("connectedmhubs/{mhubid}/{time}/{delta}")
public String getConnectedThingsExtended(#PathParam("mhubid") String strMHubID, #PathParam("time") long timestamp, #PathParam("delta") long delta){
// ...
return "{}";
}
}
Using the #Path makes the params mandatory. You can get around this with regular expressions or you can use #QueryParam with #DefaultValue to roll the two methods into one.
Using a path pattern like this:
#Path("connectedmhubs/{mhubid}")
makes the path parameter mandatory. However, you can make use of regular expressions to overcome this limitation. See this link for details.

RESTEasy can't find Message Body Writer for application/xml when using dynamically created class

Could not find MessageBodyWriter for response object of type: java.util.ArrayList of media type: application/xml
I am getting the above error when trying to return a response in xml of a list of dynamically created classes/dtos.
#GET
#Path("objects")
public Response getObjects(
#DefaultValue("json") #QueryParam("format") String format)
{
GenericEntity entity;
//I use cglib here to dynamically at runtime create a class called objectDto.
//The class is just a POJO.
List<Object> objectsDto = generateObjects(fields);
entity = new GenericEntity<List<Object>>(objectsDto){};
Response.ResponseBuilder rBuild;
if (format.equals("xml"))
{
rBuild = Response.ok(entity, MediaType.APPLICATION_XML);
}
else
{
rBuild = Response.ok(entity, MediaType.APPLICATION_JSON);
}
return rBuild.build();
}
The weird thing is I can return JSON representations of this object but not XML. Also I can return XML representations of not dynamically created classes.
I have the correct dependency in my Maven project for resteasy-jaxb-provider:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>3.0.6.Final</version>
</dependency>
This is not possible using cglib out of the box. Since the XmlRootElement is not inherited, the subclass that is created by cglib will not longer carry this annotation. Cglib itself was written before annotations were introduced to Java and no recent update added this functionality. You can instead register an ASM visitor with the cglib enhancer which should be responsible for adding the annotation to the cglib generated class.
However, you might want to consider creating your classes using javassist which has a more modern API and supports the writing of annotations.

Can I control how spring controller method arguments are instantiated?

Consider the following interface/object hierarchy in a spring project:
public interface MyInterface {
//method defenitions
}
#Component
#Scope(SCOPE_PROTOTYPE)
public class MyClass implements MyInterface {
//method implementations
}
I use MyClass in a controller method where it is read from the requests body:
#RequestMapping(method = POST, value = "/posturi", consumes = "application/json")
public void createEntity(#RequestBody MyClass myClass) {
//handle request
}
The jackson library is used to read json data and convert it to a java object.
I would like to change the type of the parameter in the controller method from MyClass to MyInterface. This does not seem to work since the interface can't be instantiated with the new operator. But it could be created like this:
MyInterface instance = applicationContext.getBean(MyInterface.class);
Is it possible to make spring/jackson instantiate the object this way? I would like to do this so that my controller does not need to be aware of what implementation is used.
It should be possible with Converters. See documentation http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/validation.html. Question is, how do you know which class you return by converter? Rather rethink your design to use POJOs in input.
I have solved this now and the concept is quite simple but the implementation can be a bit tricky. As I understand it, you can annotate any type with #RequestBody as long as you provide a HttpMessageConverter that can convert from a http request to your desired type.
So the solution is:
Implement a HttpMessageConverter
Configure spring so that your HttpMessageConverter is used.
The second part can be a bit tricky. This is because spring adds a bunch of default HttpMessageConverter that can handle common types such as strings, integers, dates and I want these to continue to function as usual. Another problem is that if jackson is on the path, spring also adds a MappingJackson2HttpMessageConverter for generic json handling such as converting to concrete objects, maps and so on. Spring will use the first HttpMessageConverter it finds that claims to be able to convert to your type. The MappingJackson2HttpMessageConverter claims to be able to do so for my objects, but it is not able to, so it fails and the request fails. This could be considered a bug...
The chain that I wanted was:
Springs default HttpMessageConverters.
My own HttpMessageConverter
The MappingJackson2HttpMessageConverter
I found two ways to acheive this. First, you can declare this explicitly through xml.
<mvc:annotation-driven>
<mvc:message-converters>
<!-- All converters in specific order here -->
</mvc:message-converters>
</mvc:annotation-driven>
The downside of this is that if the default HttpMessageConverter chain changes in later releases, it will not change for your configuration.
Another way to do it is to programatically insert your own HttpMessageConverter before the MappingJackson2HttpMessageConverter.
#Configuration
public class MyConfiguration {
#Autowired
private RequestMappingHandlerAdapter adapter;
#Autowired
private MyHttpMessageConverter myHttpMessageConverter;
#PostConstruct
private void modify() {
List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters();
int insertLocation = messageConverters.size() - 1;
for (int i = 0; i < messageConverters.size(); i++) {
Object messageConverter = messageConverters.get(i);
if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
insertLocation = i;
}
}
messageConverters.add(insertLocation, myHttpMessageConverter);
}
}
The second alternative will continue to use the "default configuration" even if it changes in later releases. I consider it a bit hacky and not at all elegant but the reason I think it is a valid soulution is that there seems to be flaws in the MappingJackson2HttpMessageConverter claiming to be able to convert to types it cannot convert to. And also that you cannot explicitly add a HttpMessageConverter to a specific position in the chain.
For now I am going with the second option but how you do is up to you...

Categories

Resources