JAX-RS JAXB JSON Response not working ( MessageBodyProviderNotFoundException) - json

I had been working for sometime to figure out how to create a JAX Restful Service... using the guide available here - Jersey
As explained in Section 2.3.2, I had added the below dependency in Maven -
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.0</version>
</dependency>
In web.xml
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.filter.LoggingFilter</param-value>
</init-param>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.hms.rs.controller.MyApp</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
and MyApp.java
public class MyApp extends Application{
#Override
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>() {{
// Add your resources.
System.out.println("From the Myapp...");
add(Patient.class);
add(PatientController.class);
// Add LoggingFilter.
add(LoggingFilter.class);
}};
}
}
Patient.java -
#XmlRootElement(name = "Patient")
public class Patient {
private String patientFName;
private String patientLName;
private int patientAge;
private String patientSex;
private String patientParentSpouse;
private String patientQual;
private String patientOccupation;
private String patientComments;
public Patient()
{
}
Setters and Getters....
}
PatientController.java -
#Path("/ManagePatient")
public class PatientController {
#GET
#Path("/getPatient")
#Produces(MediaType.APPLICATION_XML)
public Patient printPatient() {
System.out.println("Hello.... from the PatientController");
Patient ptnt = new Patient();
ptnt.setPatientFName("FirstN");
ptnt.setPatientLName("LName");
ptnt.setPatientAge(30);
ptnt.setPatientSex("M");
ptnt.setPatientParentSpouse("ParentSpuse");
ptnt.setPatientQual("engg");
ptnt.setPatientOccupation("software");
ptnt.setPatientComments("comments here");
System.out.println("Patient = " + ptnt);
//return ptnt.toString();
return ptnt;
}
When I try to access this via browser # localhost:8080/HMS_Web/services/ManagePatient/getPatient
I am getting
javax.servlet.ServletException: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=text/html, type=class com.hms.app.ui.beans.Patient, genericType=class com.hms.app.ui.beans.Patient.
and I also see the below warning in the logs-
WARNING: A provider com.hms.app.ui.beans.Patient registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider com.hms.app.ui.beans.Patient will be ignored.
If Jersey 2.0 supports JAXB based xml or json support as mentioned # section "8.1.1.2. JAXB based JSON support" in the Jersey guide, I am not sure why I am receiving the Provider errors.
Could any JAX-WS expert help me understand and also provide me direction on how to resolve this situation?
Thank you in advance

you are accessing the service via browser, so your PatientController will try to render response as html, I guess this is the reason for the
javax.servlet.ServletException: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=text/html, type=class com.hms.app.ui.beans.Patient, genericType=class com.hms.app.ui.beans.Patient.
try to comsume the service via jersey client api as following:
WebTarget webTarget = client.target("http://localhost:8080/HMS_Web/services/ManagePatient/getPatient");
Patient patient = webTarget.request(MediaType.APPLICATION_XML_TYPE).get(Patient.class);
for the warning:
WARNING: A provider com.hms.app.ui.beans.Patient registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider com.hms.app.ui.beans.Patient will be ignored.
I think you should remove:
add(Patient.class);
in your MyApp. Patient is just a POJO, it is neither a resource nor a provider.

Related

RESTEasy: #Form deserialization issue in a #POST method

Here's a puzzle!
In a simple POST implementation:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response promote(#Form PromotionForm promotion) {
return Response.status(Response.Status.OK)
.entity(promotion.toString())
.build();
}
the argument passed to me does not have values set:
PromotionForm{name='null', csid=null}
But in debugger I can see that the request server received has the values in its input stream:
new BufferedReader(new InputStreamReader(
((HttpServletInputMessage) request).getInputStream())).readLine()
// returns: {"name":"form","csid":123}
After some debugging I could see that RESTEasy tries to derive arguments for the POST method call:
args[i++] = extractor.inject(input, response);
Which leads to FormInjector code:
propertyInjector.inject(request, response, target);
And eventually to FormParamInjector:
List<String> list = request.getDecodedFormParameters().get(paramName);
But request.getDecodedFormParameters() size is 0. RESTeasy does not try to read
anything from the requests' input stream for some reason.
Any ideas how I can make RESTeasy populate PromotionForm object correctly?
More information below.
Thanks for all you answers and comments in advance.
The client call is:
final PromotionForm form = new PromotionForm();
form.setName("form");
form.setCsid(123L);
final Response response = new ResteasyClientBuilder()
.disableTrustManager()
.build()
.target(targetField.getValue())
.request(requestField.getValue())
.cookie(cookieNameField.getValue(), cookieValueField.getValue())
.buildPost(Entity.entity(form, MediaType.APPLICATION_JSON_TYPE))
.invoke();
The PromotionForm:
import javax.ws.rs.FormParam;
public class PromotionForm {
#FormParam("name")
private String name;
#FormParam("csid")
private Long csid;
// setters & getters omitted
Dependencies:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.0.4.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>3.0.4.Final</version>
</dependency>
<!-- scannotation & resteasy-client ommitted -->
web.xml snippet:
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<context-param>
<!--If the url-pattern for the Resteasy servlet-mapping is not /*-->
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</context-param>
Your method says that it consumes application/json. But the PromotionForm-class has #FormParam-annotations, which as the name implies, consumes form data.
To fix this, do one of the following:
Try to post form data instead of json. And change your #Consumes-annotation value to application/x-www-form-urlencoded
OR
Remove the #Form-annotation. Add JAXB-annotations on PromotionForm, so that the json data you post can be mapped to the PromotionForm-class.

Jersey 1.17.1 POJO Mapping Feature Server and how to get from client?

I am running a REST service on server and I want to convert my List of POJO into Json. I don't want to use #XMLRootElement JA-RX because it is only good for XML. If you Google you will find that Jackson is very good choice for Json.
Is there anyone who have solved this problem and please paste complete Server and Client Code?
Note:
I spent 16 hours in just finding out how to do this and when I replied on questions they deleted my answer so I decided to put this here to save others valueable time and I believe in Knowledge sharing.. Please if you can improve my code. I am always open to suggestions.
Detailed Reply includes Server and Client sample implementation with
JSON Marshalling and Unmarshalling
Note: Json POJO Mapping features is done using Jackson
I spent a whole day in finidng why message body write was not found. What I was doing wrong is I was using JAXB javax.xml.bind.annotation #XMLRootElement in my Jersey 1.17.1 Web Service and I was trying to unmarshall it with Jackson.
Acutally if you Google it you will find that JAXB is only good for XML but for JSON Jackson is excellent. I also forgot to put some configuration paramters in my web.xml that enable POJO Mapping feature.
Here is the snap of how you your servlet mapping should be to enable POJO mapping feature of Jackson.
<!-- WebService -->
<servlet>
<servlet-name>REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.LoggingFilter;com.algo.server.webservice.WebServiceRequestFilter</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.algo.server.webservice;org.codehaus.jackson.jaxrs</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
You also need to add those jar files into your WEB-INF/libs folder
jackson-core-asl-1.9.2.jar
jackson-mapper-asl-1.9.2.jar
jackson-xc-1.9.2.jar
jackson-jaxrs-1.9.2.jar
This is a sample web service method that returns a list of some objects
#GET
#Produces({ MediaType.APPLICATION_JSON })
#Path("/clientId/{client_id}/clientDept/{client_department}/clientLoc/{client_location}")
public Response getTasksForClientId(#PathParam("client_id") String pClientId,
#PathParam("client_department") String pClientDepartment,
#PathParam("client_location") String pClientLocation) {
List<Task> list = new ArrayList<Task>(10);
Task task = null;
for (int i = 0; i < 10; i++) {
task = new Task();
task.setComments("These are test comments");
task.setCreatedBy(11L);
task.setCreatedOn(new Date());
task.setFromDay(new Date());
task.setFromTime(new Date());
task.setToTime(new Date());
task.setToDay(new Date());
task.setUpdatedOn(new Date());
task.setLocation("Pakistan Punajb");
task.setSpecialCoverImage("webserver\\cover\\cover001.png");
task.setTargetId(1L);
task.setTargetPlaceHolder(2);
task.setUpdatedBy(23234L);
list.add(task);
}
GenericEntity<List<Task>> entity = new GenericEntity<List<Task>>(list) {
};
return Response.ok(entity).build();
}
Client Side
Now How to use convert this JSON object on client side into same List<T> Object. It's a sinch :-)
You need to put the same class from the server that you converted into POJO. It shoulb be the same
private void checkForUpdate() {
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
clientConfig.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(clientConfig);
WebResource webResource = client.resource("http://localhost:8080/some-server");
WebResource wr = webResource.path("rest").path("taskPublisherService").path("clientId/12").path("clientDept/2").path("clientLoc/USA");
ClientResponse clientResponse = wr.type(MediaType.APPLICATION_JSON).get(ClientResponse.class);
List<Task> lists = clientResponse.getEntity(new GenericType<List<Task>>() {});
System.out.println(lists);
}
This one from Jersey includes all the above mentioned JARs:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>${version.jersey}</version>
<scope>runtime</scope>
</dependency>

Jersey JAXB Based JSON support

I am trying to use Jersey's capabilities to produce JSON from my web-service methods.
Everything worked well but then I discovered that for a list of objects JSON representation contains something like enclosing root tag. I found out that I can configure JAXB Based JSON support with JSONConfiguration.natural() to produce a desirable result. So I wrote the following
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private final JAXBContext context;
private final Set<Class> types;
private final Class[] cTypes = {TrRegion.class};
public JAXBContextResolver() throws Exception {
this.types = new HashSet(Arrays.asList(cTypes));
this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), cTypes);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
return (types.contains(objectType)) ? context : null;
}
}
And plugged it in like this
public class WebServiceApplication extends Application {
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(OrderInfrastructureResource.class);
resources.add(OrderResource.class);
resources.add(JAXBContextResolver.class);
return resources;
}
}
<servlet>
<description>JAX-RS Tools Generated - Do not modify</description>
<servlet-name>JAX-RS Servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.[...].WebServiceApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
But for some reason I always get
java.lang.IllegalStateException: No JAXB provider found for the following JAXB context: class com.sun.xml.bind.v2.runtime.JAXBContextImpl
at com.sun.jersey.json.impl.JSONHelper.getJaxbProvider(JSONHelper.java:106) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.JSONHelper.getJaxbProvider(JSONHelper.java:106) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.DefaultJaxbXmlDocumentStructure.getXmlDocumentStructure(DefaultJaxbXmlDocumentStructure.java:76) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.writer.Stax2JacksonWriter.<init>(Stax2JacksonWriter.java:169) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.Stax2JsonFactory.createWriter(Stax2JsonFactory.java:105) [jersey-json-1.17.jar:1.17]
at com.sun.jersey.json.impl.provider.entity.JSONListElementProvider.writeList(JSONListElementProvider.java:133) [jersey-json-1.17.jar:1.17]
Can someone tell me why?
When I change the line
this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), cTypes);
to
this.context = new JSONJAXBContext(JSONConfiguration.mapped().build(), cTypes);
it begins to work but gives enclosing root tag(well it is the same as not specifying any ContextResolver). Strange.(Strange meaning, that the difference is only in mapping type I provide).
I try to run my app on Jboss 7.1.1 with Restesy disabled(I have removed lines <extension module="org.jboss.as.jaxrs"/> and <subsystem xmlns="urn:jboss:domain:jaxrs:1.0"/> from my standalone.xml file). Also I use com.sun.jersey.spi.spring.container.servlet.SpringServlet as Jersey servlet.
Please, tell me what am I missing.
What could be the problem?
Apparently I was missing jaxb-impl library for my application so I just added jBoss' com.sun.xml.bind module in my jboss-deployment-structure file as following:
<dependencies>
<module name="com.sun.xml.bind" />
<module name="org.codehaus.jackson.jackson-core-asl" />
<module name="org.codehaus.jackson.jackson-jaxrs" />
<module name="org.codehaus.jackson.jackson-mapper-asl" />
<module name="org.codehaus.jackson.jackson-xc" />
</dependencies>

Turn on gzip compression for grizzly under JerseyTest

I have jersey implementation of web service. The response per requirements must be gzip-ed.
Client side contains following bootstrap code to switch gzip on:
Client retval = Client.create();
retval.addFilter(
new com.sun.jersey.api.client.filter.GZIPContentEncodingFilter());
For Tomcat web.xml gzip is configured as follow
<servlet>
<display-name>JAX-RS REST Servlet</display-name>
<servlet-name>JAX-RS REST Servlet</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
</init-param>
And everything works fine!
But I need write unit test that invokes my service. I'm using JerseyTest as base and in practice way it was shown that grizzly is not correctly handles gzip without explicit declaration. I have found code snippet how to switch it on similar problem, but I have no idea how to combine it with JerseyTest.
Thank you in advance
Here is a sample test case if you're using the jersey test Framwork:
#Test
public void testGet(){
WebResource webResource = resource();
ClientResponse result = webResource
.path("pathToResource")
.header("Accept-Encoding", "gzip")
.head();
assertEquals(
"response header must contain gzip encoding",
"[gzip]",
result.getHeaders().get("Content-Encoding").toString());
}
AS the client API changed in the current Jersey versions, this is a sample test which works with Jersey 2.6:
public class WebServicesCompressionTest extends JerseyTest {
#Path("/")
public static class HelloResource {
#GET
public String getHello() {
return "Hello World!";
}
}
#Override
protected Application configure() {
enable(TestProperties.LOG_TRAFFIC);
return new ResourceConfig(
HelloResource.class,
EncodingFilter.class,
GZipEncoder.class,
DeflateEncoder.class
);
}
#Test
public void testGzip() {
Response response = target().request().acceptEncoding("gzip").get(Response.class);
assertThat(response.getStatus(), is(200));
assertThat(response.getHeaderString("Content-encoding"), is("gzip"));
}
}

Jersey implementing ContextResolver<JAXBContext> in Spring

So I am writing a Spring(2.5( + Jersey(1.1.4.1) and trying to create a JSONConfiguration using a ContextResolver. Here is the code:
package com.rhigdon.jersey.config;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.api.json.JSONJAXBContext;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
#Provider
public final class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
public JAXBContextResolver() throws Exception {
this.context = new JSONJAXBContext(JSONConfiguration.mappedJettison().build(), "com.rhigdon.core.model.");
}
public JAXBContext getContext(Class<?> aClass) {
return context;
}
}
Unfortunately my app is still returning the default mapping:
{"id":"1","question":"What is/was the
name of your first pet?"}
When I debug the application it never actually hits this code. Is this due to using the SpringServlet? Here is my Jersey Config in my web.xml:
<servlet>
<servlet-name>Jersey Spring Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Spring Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Anyone have a similar setup with JSONConfiguration working?
You need to register your provider in your spring context:
<bean class="com.company.jersey.config.JAXBContextResolver"/>
Or, if you are using annotation-based configuration, you need to annotate your provider class with #Component and include something like
<context:annotation-config />
<context:component-scan base-package="com.company.jersey" />
to your application context configuration.
I'm using jersey version 1.10 and I don't have the #Component annotation nor the bean definition, and it works without it.
Jersey REST Service
com.sun.jersey.spi.spring.container.servlet.SpringServlet
com.sun.jersey.config.property.packages
ca.gc.cbsa.ezfw.foundation.webservice
1