Using JAXB to parse JSON in Jersey - java

I'm trying to create a very simple RESTful web service using Jersey. I'm trying to make it so that it consumes and produces a JSON by using JAXB. The problem is that I get an error when I pass a JSON to it.
Below is the resource code. Both status() and echo() are working properly. Please note that on processRequest() I'm currently producing a text response, but that will be changed later to produce a JSON.
package web_service;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
#Path("/")
public class WebService {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String status() {
return "Web service is up and running.";
}
#POST
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.TEXT_PLAIN)
public String echo(String consumed) {
return consumed;
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public String processRequest(JAXBElement<Request> r) {
Request request = r.getValue();
return "Latitude: " + request.latitude +
"\n:Longitude: " + request.longitude;
}
}
This is the Request model:
package web_service;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Request {
public String latitude;
public String longitude;
public Request() {}
public Request(String latitude, String longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
// Getters and setters for both
}
My web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID"
version="2.5">
<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.packages</param-name>
<param-value>web_service</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Finally, this is an example of the POST that I'm doing (headers set to 'Content-Type: application/json');
{
"latitude":"25.764084501106787",
"longitude":"-80.37422332275389"
}
When I run this, Tomcat gives me the following Exception:
WARNING: WebApplicationException cause:
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json, type=class web_service.Request, genericType=class web_service.Request.
And I get the following response:
415 Unsupported Media Type
Content-Length: 1 kB
Content-Type: text/html;charset=utf-8
Date: 2013 Nov 4 17:41:20
Server: Apache-Coyote/1.1
I'm very new to this and this is not making a lot of sense. Hopefully, one of you will be able to give me a hand. Thanks!

Add this to your web.xml
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
For more details look at jersey documentation : https://jersey.java.net/documentation/1.18/json.html

Try this in your endpoint:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public String processRequest(Coordinates coordinates) {
return "Latitude: " + coordinates.getLatitude() +
"\n:Longitude: " + coordinates.getLongitude();
}
where Coordinates is a simple POJO mirroring the JSON content you are posting.
Then use Jackson, which has JAXB support, by adding all the libraries to your project and adding this configuration:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.foobar.rest.services;org.codehaus.jackson.jaxrs</param-value>
</init-param>
to the "Jersey REST Service" servlet.

JAXB (Java Architecture for XML Binding) avaialable in java for binding xml not for JSON.Here you are trying to convert JavaScript Object Notation with the help of xml binder in this situation and that is not possible. And i am pretty sure that you must be getting the error generated by " BodyWriter class " in the JAX-RS package....
Anyway If you want to produce and consume JSON using your resource you gonna need "moxy" JAR available in your project library for handing this conversion :)
Hope what i am writing here will be helpful for other programmers

this error happends when you are requesting without specified headers.
In your service
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
Ensure that your request gets the header "content-type" ? application/json and the header "accept" contains text/html

I think, there is problem with your Request model class. Your model class is not specifying #XmlaccessType, so by default it is being considered as PUBLIC_MEMBER. As your instance variables are also public along with getters and setters, So JAXB is not able to figure out proper bindings.That's why that exception is coming. So there are two fixes to your problem.
1)You can make your instance variables as private.
2)Or you can explicitly specify #XmlaccessType for your model class and likewise provide annotations in it which are non-conflicting.

Related

Java REST service generates 405 on POST

I am developing a JAX-RS REST service. I'm using Java 1.8, Tomcat 7, and invoking my POST through JQuery/AJAX.
My JQuery code is very simple:
$.post(ctx + "/addressinfo/", data, null, "json")
where data is a simple JSON object containing basic address information.
My REST controller is as follows:
package addressservice.controller;
import javax.servlet.*;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.goodyear.menuadmin.plantservice.model.PlantInfo;
import com.goodyear.menuadmin.plantservice.service.PlantInfoService;
#Path("/")
public class AddressController {
private AddressService service = new AddressService ();
private Address address;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAddress(#Context ServletContext ctx) throws Exception {
this.address= plantService.getInfo("Name");
return Response.ok(this.plant, MediaType.APPLICATION_JSON).build();
}
#POST
#Consumes(MediaType.MEDIA_TYPE_WILDCARD)
private Response saveAddress(Address data) {
boolean isOk = addressService.updateInfo(data);
if(!isOk) {
Response.serverError().build();
}
return Response.ok(data, MediaType.APPLICATION_JSON).build();
}
}
However, when I execute this, I am receiving a 405 Method Not Allowed error. The post is initiated from a button click on a form.
The GET portion works just fine.
The response headers are:
HTTP/1.1 405 Method Not Allowed
Server: Apache-Coyote/1.1
Allow: HEAD,GET,OPTIONS
Content-Length: 0
Date: Mon, 10 Sep 2018 17:58:08 GMT
I've tried several different attempts to correct this ranging from adding this init parameter to my tomcat web.xml
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
I've tried changing from $.post to the following:
return $.ajax({
url: ctx + "/addressinfo/",
type: 'post',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(data)
});
EDIT
I neglected to add the servlet definition for the application's web.xml
<servlet>
<servlet-name>Address Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>addressservice.controller</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>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Plant Service</servlet-name>
<url-pattern>/addressinfo/*</url-pattern>
</servlet-mapping>
Nothing seems to be allowing me to post to my REST service.
Is there something I need to change in my Tomcat config? Or Am I doing something else wrong? I suppose I could change from a REST service to a servlet, But I would prefer to not have to do that.
Your Java method for #POST is private, it should be public. Jersey ignores private methods in Controllers.
saveAddress only consumes data doesn't produce
so implement
#Produces
in saveAddress as well

jersey/tomcat Description The origin server did not find a current representation for the target resource

I have configured jersey (with tomcat server) according to the details described until step 6.3 here:
http://www.vogella.com/tutorials/REST/article.html#jerseyprojectsetup
Below is my web.xml file, the only difference is that the path/package name for the jersey.config.server.provider.packages
the instructions has;
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.vogella.jersey.first</param-value>
because com.vogella.jersey.first is their package name, while I chose to point mine to my default package (jerseytest)
but this is my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>jerseytest</display-name>
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<!-- Register resources and providers under com.vogella.jersey.first package. -->
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>jerseytest</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
when I run the application on the tomcat server I keep getting:
HTTP Status 404 – Not Found
Type Status Report
Message /jerseytest/
Description The origin server did not find a current representation
for the target resource or is not willing to disclose that one exists.
Apache Tomcat/8.5.23
and when i point the browser to the end point mentioned in the tutorial :
http://localhost:8080/com.vogella.jersey.first/rest/hello
I get a not found error:
HTTP Status 404 – Not Found
Type Status Report
Message Not Found
Description The origin server did not find a current representation
for the target resource or is not willing to disclose that one exists.
Apache Tomcat/8.5.23
When I point the browser directly to http://localhost:8080/rest/hello
I get:
HTTP Status 404 – Not Found
Type Status Report
Message /rest/hello
Description The origin server did not find a current representation
for the target resource or is not willing to disclose that one exists.
Apache Tomcat/8.5.23
My java class is identical to the example in the instructions , except that mine is in the default package :
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
// Plain old Java Object it does not extend as class or implements
// an interface
// The class registers its methods for the HTTP GET request using the #GET annotation.
// Using the #Produces annotation, it defines that it can deliver several MIME types,
// text, XML and HTML.
// The browser requests per default the HTML MIME type.
//Sets the path to base URL + /hello
#Path("/hello")
public class Hello {
// This method is called if TEXT_PLAIN is request
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
return "Hello Jersey";
}
// This method is called if XML is request
#GET
#Produces(MediaType.TEXT_XML)
public String sayXMLHello() {
return "<?xml version=\"1.0\"?>" + "<hello> Hello Jersey" + "</hello>";
}
// This method is called if HTML is request
#GET
#Produces(MediaType.TEXT_HTML)
public String sayHtmlHello() {
return "<html> " + "<title>" + "Hello Jersey" + "</title>"
+ "<body><h1>" + "Hello Jersey" + "</body></h1>" + "</html> ";
}
}
I struggled with this problem many times.
The solution I am currently using is weather the webapp(or the folder where you kept the views like jsp) is under deployment assembly.
To do so
Right click on the project > Build Path > Configure Build path > Deployment Assembly > Add(right hand side) > Folder > (add your jsp folder. In default case it is src/main/webapp)
Each method needs to specify the path
like this :
#GET
#Path("testOne")
#Produces(MediaType.TEXT_HTML)

Spring WS without xsd

I'trying to create simple WS project in Spring and Spring WS without any XSD. Deploy on jetty.
Is possible to populate WS endpoint and generate WSDL only from java classes (no static XSD or WSDL - I went throught many tutorials but all requiered).
For any help, hint or link highly appreciated.
I have something like this:
1) Request
#XmlRootElement
public class MessageWSRequest {
#XmlElement
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
2) Endpoint
#Endpoint
public class MessageWS {
#PayloadRoot(namespace = "http://message.com/ws/message" ,localPart="MessageWSRequest")
public String handleMathServiceRequest(#RequestPayload MessageWSRequest messageWSRequest) {
return "ok";
}
}
3) springContext.xml
<sws:annotation-driven/>
<context:component-scan base-package="com.ws.message"/>
4) web.xml
<servlet>
<servlet-name>webservices</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>webservices</servlet-name>
<url-pattern>*.wsdl</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>webservices</servlet-name>
<url-pattern>/endpoints/*</url-pattern>
</servlet-mapping>
Now I would expect URL like this
localhost:8080/messageTest/endpoints/MessageWS.wsdl
with generated WSDL.
Did I miss some configuration or so?
Thanks all
Ok, next day a clear mind revelead me this fact:
Spring WS offers "only" contract-first, starting from an XSD Schema
I'll use CXF instead:
Apache CXF offers both contract-last (starting with Java) and Contract-first (starting with the WSDL) approaches.
As you have noted, Spring WS is designed for contract first services. However I think that you can still achieve what you want to do if you generate the XSD during the build process from your annotated classes. Here is one way to do that:
Generating XSD schemas from JAXB types in Maven?

Jaxb + jersey. Skipping null fields in generated json

I have a code:
return Response.status(Status.BAD_REQUEST).entity(errors).build();
Where: Response is comes from this package: javax.ws.rs.core (jersey 1.9.1);
Where the errors is instance of:
#XmlRootElement
public class UserInfoValidationErrors {
#XmlElement String username;
#XmlElement String email;
...
Then I have JSON result like this: {"username":null,"email":"Email is not valid"}
If there is a way how to avoid having null there?
If you have Jersey configured to use Jackson to do it's JSON serialization, you can annotate your model classes with:
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
If you want to configure Jersey to use Jackson, you can update your web.xml as follows:
<servlet-name>Jersey</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.your.package;org.codehaus.jackson.jaxrs</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Use #XmlElement(nillable = true). At least this works for XML generation, so I believe it should work for JSON as well.

Jersey: redirect outside of Jersey root

I've been writing a prototype Jersey (JAX-RS) application and wanted to try handling application/x-www-form-urlencoded posts with a redirect-after-POST methodology.
I want to redirect to an html page hosted at the application root on success, however I can't seem to escape out of Jersey's servlet root.
Here's an example of a resource which allows you to create a new user:
URI I want: /jersey-test/user.html
URI I get: /jersey-test/r/user.html
#POST
#Consumes({MediaType.APPLICATION_FORM_URLENCODED})
public Response putUser(#Context UriInfo uriInfo,
MultivaluedMap<String, String> formParams) {
// snip... do work and insert user here...
URI uri = uriInfo.getBaseUriBuilder().path("user.html").build();
return Response.seeOther(uri).build();
}
Relevant snippets from my web.xml:
<web-app ...>
<display-name>jersey-test</display-name>
...
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
...
</servlet>
...
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/r/*</url-pattern>
</servlet-mapping>
</web-app>
Assign the path like this:
URI uri = uriInfo.getBaseUriBuilder().path("../user.html").build();

Categories