In my Introduction to Java Web Services course I use as an example a web service that returns a List of JPA entity objects of type Fish. A Fish is just several fields that describes different aspects of aquarium fish. For many years, going back to 2004, I only showed SOAP services. The code generator in my favourite IDE, NetBeans, output working server and client code. Then about five years ago I added REST services to my curriculum.
The NetBeans code generator for REST services produced workable code except in one situation for REST clients. The List type could not be returned by the generated code. Here is what the client generated code looks like:
[pastacode lang=”java” manual=”public%20%3CT%3E%20T%20getJson(Class%3CT%3E%20responseType)%20throws%20ClientErrorException%20%7B%0A%20%20%20%20WebTarget%20resource%20%3D%20webTarget%3B%0A%20%20%20%20return%20resource.request(javax.ws.rs.core.MediaType.APPLICATION_JSON).get(responseType)%3B%0A%7D%0A” message=”REST client method” highlight=”” provider=”manual”/]
To call this code I used:
[pastacode lang=”java” manual=”List%3CFish%3E%20fishies%20%3D%20client.getJson(List.class)%3B” message=”Calling the REST service” highlight=”” provider=”manual”/]
When I ran this code, I received the following error message:
[pastacode lang=”markup” manual=”org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException%3A%20MessageBodyReader%20not%20found%20for%20media%20type%3Dapplication%2Fjson%2C%20type%3Dinterface%20java.util.List%2C%20genericType%3Dinterface%20java.util.List.” message=”Error message” highlight=”” provider=”manual”/]
This error has been around from the first time I explored REST services. After spending a few hours looking for a solution five years ago I came across one that showed by adding a dependency to my pom.xml the code will work.
[pastacode lang=”markup” manual=”%3Cdependency%3E%0A%20%20%20%20%3CgroupId%3Ecom.owlike%3C%2FgroupId%3E%0A%20%20%20%20%3CartifactId%3Egenson%3C%2FartifactId%3E%0A%20%20%20%20%3Cversion%3E1.4%3C%2Fversion%3E%0A%3C%2Fdependency%3E%0A” message=”Maven dependency” highlight=”” provider=”manual”/]
Genson, a Java and Scala JSON conversion library, was created to deal with several issues in REST services, one of which was to simplify working with List. You can learn more about this library at https://owlike.github.io/genson/. I added the dependency to the client pom file and never gave it another thought until today. How could jax-rs not be able to handle a List? So, I did more research.
I discovered that this was a problem that many had encountered and not just NetBeans users. There were several suggested solutions, but the one that made the most sense showed me that the problem was not with jax-rs but with the code that NetBeans generated.
The solution came from Adam Bien’s blog. Adam is one of the foremost Java EE developers and trainers who is in great demand as a speaker. I have had the good fortune of meeting him at JavaOne. His blog at http://www.adam-bien.com/roller/abien/entry/jax_rs_returning_a_list showed that responses from a REST service that are in a List must be wrapped in a GenericType class. First, I changed the generated getJson method, renaming it getJson2:
[pastacode lang=”java” manual=”public%20List%3CFish%3E%20getJson2()%20throws%20ClientErrorException%20%7B%0A%20%20%20%20WebTarget%20resource%20%3D%20webTarget%3B%0A%20%20%20%20return%20resource.request(javax.ws.rs.core.MediaType.APPLICATION_JSON)%0A%20%20%20%20%20%20%20%20.get(new%20GenericType%3CList%3CFish%3E%3E()%20%7B%20%7D)%3B%0A%7D%0A” message=”Corrected method for returning a List” highlight=”4″ provider=”manual”/]
If you compare this to the generated code shown earlier in this article you will see that this method is now tightly coupled to the data type it is returning. The generated code was designed to work in all cases because it used generics. As generated, you needed to pass the class of the returned data. Problem is that you cannot pass a generic type such as List<Fish>.class instead of List.class.
When the return type is a generic collection, Adam pointed out that it must be wrapped in a GenericType class that is part of jax-rs. The code that NetBeans produces should work in all cases but not in this case.
The dependency for Genson is now removed and my executable client is 378K smaller.
Thanks to Adam Bien for his article. Read his blog at http://www.adam-bien.com and follow him on Twitter at @AdamBien