I'm using Spring Data Repository to persistence objects. I'm trying to return them by REST web api (Jersey) and getting an error, but when i'm trying to return normal POJO object, all looks fine, Jersey parse object to JSON and i'm getting response 200 code.
#POST
#Path("/test")
#Produces( MediaType.APPLICATION_JSON )
public Document test() {
Document d = documentRepository.findOne(123L);
return d; // response code 500 without any stack trace
return new Document(); // normal JSON object in response content
}
My dependencies for jersey:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.16</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>2.16</version>
</dependency>
web.xml:
<servlet>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
How can i return proxy object from spring data repository, or List of these objects ?
Related
I'm new RESTful web services and have been playing around with Jersey and Heroku (which uses a Jetty stack). I'm writing a simple REST API which returns a Map<String,String> in JSON for a GET request.
I'm however running into a 500 eror. The error message is :
HTTP Status 500 - org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/json, type=class java.util.LinkedHashMap, genericType=java.util.HashMap.
Below is the code snippet for my resource :
#GET
#Produces("application/json")
public HashMap<String,String> getIt() {
HashMap<String,String> nameValue = new LinkedHashMap<String,String>();
nameValue.put("Alpha","One");
return nameValue;
}
Below is my web.xml file:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>org.example.services</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Here is my Main class
public class Main {
public static void main(String[] args) throws Exception{
// The port that we should run on can be set into an environment variable
// Look for that variable and default to 8080 if it isn't there.
String webPort = System.getenv("PORT");
if (webPort == null || webPort.isEmpty()) {
webPort = "8080";
}
final Server server = new Server(Integer.valueOf(webPort));
final WebAppContext root = new WebAppContext();
root.setContextPath("/");
// Parent loader priority is a class loader setting that Jetty accepts.
// By default Jetty will behave like most web containers in that it will
// allow your application to replace non-server libraries that are part of the
// container. Setting parent loader priority to true changes this behavior.
// Read more here: http://wiki.eclipse.org/Jetty/Reference/Jetty_Classloading
root.setParentLoaderPriority(true);
final String webappDirLocation = "src/main/webapp/";
root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml");
root.setResourceBase(webappDirLocation);
server.setHandler(root);
server.start();
server.join();
}
}
Even after browsing through previous Stackoverflow answers like this or this, I could not find a way to solve my problem as they do not address Jersey 2.x with Jetty. I've added the following to my pom.xml file, however the problem still persists as unable to register the JSON bindings with the Jetty server.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
Automatic registration of providers by moxy didn't work as stated by Jersey Reference.
As per what they have stated, only moxy and jackson has POJO to JSON conversion feature.
Documentation says Jackson doesn't auto register(Not a problem any way!)
1. Swap Moxy with Jackson in POM.XML
Remove :
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>${jersey.version}</version>
</dependency>
Add:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
2. Register Jackson Message Body Readers and Writers :
Add org.codehaus.jackson.jaxrs to provider packages list. Here is my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>Web app name</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.myorg.myproj.api;org.codehaus.jackson.jaxrs</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Web app name</servlet-name>
<url-pattern>/v1/*</url-pattern>
</servlet-mapping>
P.S.
I am not promoting jackson, just that moxy didn't work for me, its writers failed to auto register as they advertised and could not find documentation about manual registration!
If using Jackson Implementation
As per the Jersey Documentation - Note that there is a difference in namespaces between Jackson 1.x (org.codehaus.jackson) and Jackson 2.x (com.fasterxml.jackson).
If you are using jackson 2.x, you need to register com.fasterxml.jackson.jaxrs as init param to the ServletContainer in your web.xml as follows:
<servlet>
<servlet-name>RESTServlet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.fasterxml.jackson.jaxrs</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
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.
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>
I get an HTTP Bad Request when trying to reach my REST API. The issue is that Spring does not give much information to debug.
The URL is http://localhost:8080/webapp/network/v1/jobs.json?callback=jQuery203040624934318475425_1377165238418&sEcho=1&iColumns=1&sColumns=&iDisplayStart=0&iDisplayLength=10&mDataProp_0=id&sSearch=&bRegex=false&sSearch_0=&bRegex_0=false&bSearchable_0=true&iSortCol_0=0&sSortDir_0=asc&iSortingCols=1&bSortable_0=true&_=1377165238419
The controller:
#Controller
#RequestMapping("/network/v1/jobs")
public class JobsController {
#RequestMapping(value = "", method = RequestMethod.GET)
public ResponseEntity<JQueryDatatablesPage<Job>> list(
#RequestParam int iDisplayStart, #RequestParam int iDisplayLength,
#RequestParam int sEcho, #RequestParam String search) {
...
}
}
In my pom file, I have jackson:
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
The json extension is used to return JSONP:
<filter>
<filter-name>jsonpCallbackFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>jsonpCallbackFilter</filter-name>
<url-pattern>*.json</url-pattern>
</filter-mapping>
The debugger stopped in the filter but I still get a Bad Request, and the debugger does not stop in the list method.
Stupid mistake parameter search was not in the URL. Just added #RequestParam(required = false) String search)
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.