Getting JAX-RS service to create an object with a JsonObject property - java

I'd like to get JAXRS to push all json details for a specific class property into a JsonObject object.
Let's say I have the following class:
public class MyMessage implements Serializable {
private PayloadType payloadType;
private JsonObject payload;
}
REST method of:
#POST
#Path("/send")
#Consumes(MediaType.APPLICATION_JSON)
public Response send(MyMessage message)
I'd like to POST the following JSON, but have the payload property set as a javax.json.JsonObject object.
{
payloadType:'MESSAGE',
payload:{
subject:"My Subject",
body:"This is a message"
}
}
I'm running on Glassfish, so I was expecting that message reader for JsonObject were included with org.glassfish.jersey.media, which is support to be included in the GF4.1. Add the following maven dependency just causes ambiguous class exceptions.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>2.22.1</version>
</dependency>

So there are a couple things stopping you here.
javax.json (or JSONP) does not handle POJOs. It only handles the javax.json API. If you what you are expecting is that the original provider handle the POJO, while the JSONP provider handle the javax.json, it doesn't work like that. Either you will use the one that handles the POJOs (which doesn't know javax.json or you use the one that handles javax.json. (We do make this happen below though :-)
Glassfish's default provider is MOXy. So we need to disable to to use any other provider. To disable MOXy, you need to set this property
ServerProperties.MOXY_JSON_FEATURE_DISABLE
to true. Either in your web.xml or your Java config.
So to make this work, we should make sure that we are using Jackson. Jackson has the jackson-datatype-jsr353 module which allows to do exactly what you are trying to do, javax.json in POJO.
Glassfish has the Jackson provider already, but you should add it anyway in a provided scope. So add these two dependencies
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.10.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr353</artifactId>
<version>2.3.3</version>
</dependency>
If yoo pay attention to the versions, I am using the same versions that are included in Glassfish 4.1. It uses Jersey 2.10.4 and Jackson 2.3.3. You want the versions to conflict. Even though the jersey-media-json-jackson is provided, it's still a good idea to try and use the same version as the server, when compiling.
Also you should register the JacksonFeature with your application.
And the last thing we need is to register the JSR353Module so that we can get the javax.json support with Jackson. To do that just register the following provider with your application.
#Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperProvider() {
mapper = new ObjectMapper();
mapper.registerModule(new JSR353Module());
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
That's it. I've tested it, and it should work.

Related

Can't retrieve JSONObject from my rest api

So I recently moved from Jersey 1.x to 2.x and after a long list of problems finaly got it working. But whenever I try to reach a resource which returns a JSONObject I get problems. First of, here is my example method:
#GET
#Path("/foobar")
#Produces(MediaType.APPLICATION_JSON)
public JSONObject print2() throws JSONException {
JSONObject jsonObject = new JSONObject();
jsonObject.put("hi", 22);
return jsonObject;
}
Now if I use Jettison 1.3.8 for my JSONObject, I get the following if I try to reach this resource:
{"escapeForwardSlashAlways":true}
Not sure whats going on there. Then I tried some older versions of Jettison and also the org.json but these gives me this issue instead:
No serializer found for class org.json.JSONObject and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )
Not sure why I get these problems when this kind of method worked fine for me on Jersey 1.x.
Assuming you are using Servlet 3.0 and above, the following example might help you to setup your environment to work with JSON data:
Dependency: if you are using Maven you need the following dependencies:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.23.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.23.2</version>
</dependency>
If you are not using Maven, you need to add the correponding jars into your classpath.
Define POJOs to contain the data you want to serialize to JSON, for example,
public class User {
private String username;
private String email;
// getters + setters
}
Modify your resource method accordingly:
#GET
#Path("/foobar")
#Produces(MediaType.APPLICATION_JSON)
public User print2() {
User jsonObject = new User();
jsonObject.setUsername("Me");
jsonObject.setEmail("my#email.com");
return jsonObject;
}
Package and deploy, and the output should be:
{
"username": "Me",
"email": "my#email.com"
}
Note: This example is deployed and works on Tomcat 8.5.5.
I was struggling with the same issue, and eventually Jersey's bookmark exmple helped.
The problem is that your Jersey has no serializer for JSONObject and it tries to use BeanSerializer instead. Jettison JSONObject has only one public getter (isEscapeForwardSlashAlways) and org.json.JSONObject has no getters at all so BeanSerializer cannot be applied.
The solution is for (jettison json object):
Add dependency jersey-media-json-jettison:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jettison</artifactId>
<version>2.26</version>
</dependency>
Register the jettison feature declaratively in your web.xml
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.jettison.JettisonFeature</param-value>
<init-param>
Or programmatically in your application:
#ApplicationPath("/")
public class MyApplication extends ResourceConfig {
public MyApplication() {
registerClasses(UsersResource.class);
register(new JettisonFeature());
}
}
web.xml:
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.glassfish.jersey.examples.bookmark.MyApplication</param-value>
</init-param>
Perhaps org.json.JSONObject has such serializer feature for Jersey too, I don't know...
Another option is to allow the Response to convert your object to JSON. This gives the added benefit of adding the HTTP code as well. So you can return a 400, 404, 500 etc. and still send back a JSON response that can be acted upon by your JS. You should be able to drop your JSONObject in there since it's basically just extended Map - or any object for that matter.
#GET
#Path("/foobar")
#Produces(MediaType.APPLICATION_JSON)
public Response print2() {
User jsonObject = new User();
jsonObject.setUsername("Me");
jsonObject.setEmail("my#email.com");
return Response.status(Response.Status.OK).entity(jsonObject).build());
}

Using a Hystrix Java Servlet & Servlet Filter in Jersey 2

I'm using Netflix' Hystrix libraries to act as a circuit breaker when connecting to remote services in a REST client I am building. I would like to setup the event streaming and dashboard monitoring via the libraries they provide. Looking at their example application here, it appears that I need to apply their servlet filters and servlet classes to my web application.
I'm using Spring Boot with Jersey 2 and wiring up my resources and filters in a JerseyConfig.java (no web.xml). I know that Jersey Filters are not the same as Servlet Filters and am struggling to integrate the two together.
So, how do you take a Java Servlet Filter and make it work as a Jersey Filter and how do you take a Java Servlet and make it work as a Jersey Resource?
My current strategy for the Servlets is to wrap them like so. One for each.
#Path("/hystrix.stream")
public class HystrixResource extends HystrixUtilizationSseServlet {
#Context
HttpServletRequest httpRequest;
#Context
HttpServletResponse httpResponse;
//This returns void because it is a text/stream output that must remain open,
//so the httpResponse is continually written to until the conenction is closed
#GET
public void doGet() throws ServletException, IOException {
doGet(httpRequest, httpResponse);
}
}
This might be working, but the data is basically empty for some reason. I am guessing that reason is because the Filters are not working.
data: {"type":"HystrixUtilization","commands":{},"threadpools":{}}
It is less clear to me how to wrap the Servlet Filters because they expect different inputs and outputs than a Jersey ContainerRequestFilter. The following implementation in my JerseyConfig seems to do nothing because the logs are not indicating that the filters are being registered and I cannot break on lines in these files in debug mode.
#Component
#ApplicationPath("/")
public class JerseyConfig extends ResourceConfig {
private static final Logger LOGGER = Logger.getLogger("JerseyConfig");
public JerseyConfig(){
//filter to provide a bridge between JAX-RS and Spring request attributes
register(RequestContextFilter.class);
register(SpringComponentProvider.class);
//handles custom serialization
register(new ObjectMapperContextResolver());
//try to register the filters - which doesn't work because these aren't Jersey Filters
register(HystrixRequestContextServletFilter.class);
register(HystrixRequestLogViaResponseHeaderServletFilter.class);
registerResources();
/*
* Enable the logging filter to see the HTTP response for each request.
*/
register(new LoggingFilter(LOGGER, true));
}
}
Servlets and Servlet filters should not be registered in the Jersey config. They will simply be ignored. You should instead be registering them with Spring Boot with ServletRegistrationBeans and FilterRegistrationBeans.
In you Spring configuration, you can do something like
#Bean
public ServletRegistrationBean someServlet() {
ServletRegistrationBean registration = ServletRegisrationBean(
new HystrixMetricsStreamServlet(), "/hystrix.stream");
registration.setName("HystrixMetricsStreamServlet");
return registration;
}
#Bean
public FilterRegistrationBean someFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new HystrixRequestContextServletFilter());
registration.setUrlPatterns(Arrays.asList("/*"));
registration.setName("HystrixRequestContextServletFilter");
// you can also set the order of filters if you need to
return registration;
}
Also:
you don't need to register the SpringComponentProvider. This is automatically registered.
If you get a 404 on trying to access the servlet being registered this way, it will be because you are using the default Jersey mapping /*, which hogs up all the request. You can change the mapping or register Jersey as a filter to forward not found requests. See this post
An alternative route, and the one I ended up eventually going with, is to use the Spring cloud/boot starters if you're in a Spring Boot project. This prevented me from having to explicitly define beans and filters as shown in the other answer. Eventually basically worked out of the box.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<exclusions>
<!--We're running our Jersey server w/ Jackson 2. This import uses Jackson 1.x and creates a breaking conflict.-->
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Reference Circuit Breaker getting started guide. The one issue I faced was the Jackson 1 vs Jackson 2 conflict and was able to add the library exclusion. I basically had the Hystrix library jar before, but nothing wired up to make it work.

Glassfish :MessageBodyProviderNotFoundException in Jersy Client

Hi All I was trying to create a rest web-service from scratch. Here is my Service part
#Path("/Phones")
public class PhonessResource {
#GET
#Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
public Response getAllNumbers(){
List<PhoneDetail> list = PhoneDirectoryDao.getInstance().getAllNumbers();
GenericEntity<List<PhoneDetail>> entity = new GenericEntity<List<PhoneDetail>>(list) {};
Response response =Response.ok(entity).status(200).build();
return response;//PhoneDirectoryDao.getInstance().getAllNumbers();
}
}
My data Model : I a had my getters and setters along with another constructor that take all property ,I didn't paste it to less the question length ,I use the same data model in client and server
#XmlRootElement(name="PhoneDetail")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder={"id","firstName","lastName","address","phoneNo","timeStamp"})
public class PhoneDetail {
private int id;
private String firstName;
private String lastName;
private String address;
private String phoneNo;
private Timestamp timeStamp;
public PhoneDetail() {}
}
Then I create a java client to test the service .I am using NETBEANS IDE ,so I choose default option in IDE to create it
Thus I create a Jersey Client
public class PhoneCLient {
private WebTarget webTarget;
private Client client;
private static final String BASE_URI = "http://localhost:8080/Phones/webresources";
public PhoneCLient() {
client = javax.ws.rs.client.ClientBuilder.newClient();
webTarget = client.target(BASE_URI).path("Items");
}
public <T> T getAllNumbers_XML(Class<T> responseType) throws ClientErrorException {
WebTarget resource = webTarget;
return resource.request(javax.ws.rs.core.MediaType.APPLICATION_XML).get(responseType);
}
public <T> T getAllNumbers_JSON(Class<T> responseType) throws ClientErrorException {
WebTarget resource = webTarget;
return resource.request(javax.ws.rs.core.MediaType.APPLICATION_JSON).get(responseType);
}
}
But It gives me this error
Exception in thread "main" org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/xml, type=class org.glassfish.jersey.client.ClientResponse, genericType=class org.glassfish.jersey.client.ClientResponse.
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:173)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:134)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:988)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:833)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:768)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:96)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:740)
at org.glassfish.jersey.client.JerseyInvocation.access$500(JerseyInvocation.java:88)
at org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.java:650)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:421)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:646)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:375)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:275)
at PhoneDirectoryClient.rest.PhoneCLient.getAllNumbers_XML(PhoneCLient.java:45)
But when I test the service in Browser or RestClient Browser plugin it works fine .
Can anybody tell me what went wrong ??
For Xml, if you have all the dependencies that come with Jersey, it should work out the box for the client API. You might not have added all of them. I see you aren't using Maven, which I would strongly advise doing. But I'll provide both way to handle this.
XML
Maven:
Only dependencies you'll nee to get the client up and running (with JAXB xml support)
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.13</version>
</dependency>
Doesn't get much simpler :-)
Non-Maven:
So using a Maven project, I added the above dependency, and these are all the transitive dependencies it pulled in. In your non Maven project, you will need to manually add all these jars.
If you go to the Jersey Hompage, go to Downloads, and download the "Jersey JAX-RS 2.0 RI bundle. You should find all these dependencies in there. You should add all the ones needed, into your project
Note: Netbeans already comes with the Jersey 2.0 (JAX-RS RI) library. You could instead simply add that library to your project. Just right click on the [Libraries] node in your project, and select [Add Library]. You should see the Jersey in the dialog. This solution is probably the easiest, but it will import all the Jersey dependencies, more than is required for the client API
JSON
JSON requires another dependency:
Maven:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.13</version>
</dependency>
Non-Maven
Have a look at this post for an image and further explanation.
Just having these dependencies on the classpath should work, without any special configuration.
UPDATE
In newer versions of the Jersey client, jersey-media-jaxb is not pulled in anymore and you will need to manually add it for XML/JAXB support.

Post data Spring web services Restful

I am trying to save data on my database with a web service POST wich serializes a HTML form to save a object. The rest client firefox says this:
"The server refused this request because the requested entity is in a format not supported by the requested resource for the requested method"
The eclipse console shows the message:
org.jasig.cas.client.util.CommonUtils - safeGetParameter called on a POST HttpServletRequest for LogoutRequest. Cannot complete check safely. Reverting to standard behavior for this Parameter
I understand that the object that i want to save is not valid, but I don't see what the problem is.
#RequestMapping(value="/solicitudCita", method = RequestMethod.POST)
public #ResponseBody String putSolicitud(#ModelAttribute("Solicitud") Solicitud solicitud) throws Exception{
System.out.println(solicitud.toString());
solicitudCitaAppMService.createOrUpdate(solicitud);
String solicitudAdded = "Solicitud de cita -> {" + solicitud.toString() + "} aƱadida";
System.out.println(solicitud);
return solicitudAdded;
}
Help me please
Thanks
If you want to call this controller in a RESTful manner, you have to annotate the solicitud parameter as #RequestBody. Second, you have to have the Jackson libraries in you classpath so Spring can pick them up and use them for unmarshalling the object.
If you use Maven, use these dependencies:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
BTW, why do you have to serialize the HTML form and sent it across? I would suggest you use a REST client, for instance this one, that is available in the Chrome WebStore.

How do I register the Jackson provider with the Wink client?

I'm trying to set up a toy application (which may turn in to a real application someday). I'm running into a problem with Wink and Jackson. I've got two applications: one runs wink-server on jetty and seems to be providing some JSON data just fine; one runs wink-client on jetty and receives the JSON data just fine. The problem lies in automagically deserializing the JSON data back into my Java bean.
Here's the code I use in my wink client action:
RestClient client = new RestClient();
Resource resource = client.resource("http://localhost:8081/helloworld");
User user = resource.accept(MediaType.APPLICATION_JSON).get(User.class);
Here's the error I receive when I try to run the Struts action:
java.lang.RuntimeException: No javax.ws.rs.ext.MessageBodyReader found for type class my.package.structure.User and media type application/json. Verify that all entity providers are correctly registered.
org.apache.wink.client.internal.handlers.ClientResponseImpl.readEntity(ClientResponseImpl.java:123)
org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:65)
org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:52)
org.apache.wink.client.internal.ResourceImpl.invoke(ResourceImpl.java:186)
org.apache.wink.client.internal.ResourceImpl.get(ResourceImpl.java:294)
my.package.structure.action.HelloWorldAction.execute(HelloWorldAction.java:29)
...
If I replace the last line in the first code snippet with the following line, everything works fine and dandy.
String message = resource.accept(MediaType.APPLICATION_JSON).get(String.class);
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(message, User.class);
It's clear that the data is getting across just fine, but the problem seems to lie with the fact that the JacksonJsonProvider class is not registered with Wink client. I've seen a lot of ways to register the provider with the Wink server, but not the Wink client.
Is it possible to do make the first code snippet operate properly? If so, how?
(As an aside, the other problem may be that I'm missing annotations on my User class. Right now there aren't any. Maybe I need some...)
Step 1: Create a class that extends javax.ws.rs.core.Application that allows you to set singletons.
import java.util.Collections;
import java.util.Set;
import javax.ws.rs.core.Application;
public class ClientApplication extends Application {
private Set<Object> singletons = Collections.emptySet();
#Override
public Set<Object> getSingletons() {
return singletons;
}
public void setSingletons(final Set<Object> singletons) {
this.singletons = singletons;
}
}
Step 2: In your action, create a org.apache.wink.client.ClientConfig for your org.apache.wink.client.RestClient. This allows you add the org.codehaus.jackson.jaxrs.JacksonJsonProvider to your providers list.
ClientApplication clientApplication = new ClientApplication();
Set<Object> s = new HashSet<Object>();
s.add(new JacksonJsonProvider());
clientApplication.setSingletons(s);
ClientConfig clientConfig = new ClientConfig().applications(clientApplication);
RestClient restClient = new RestClient(clientConfig);
Step 3: Create the org.apache.wink.client.Resource, use the get(Class<T> responseEntity) method and everything will now work as expected.
Resource resource = client.resource("http://localhost:8081/helloworld");
User user = resource.accept(MediaType.APPLICATION_JSON).get(User.class);
If you want to be really slick about it, you can use Spring to set up a ClientConfig bean and inject it in to your actions. Then, you can just call new RestClient(clientConfig) every time and not worry about replicating the entire setup.
i ran into this issue when trying to write some integration tests that POST an object for my rest plugin.
Rather then spinning out a new class you can provide the Jackson provider with an inline class.
#Before
public void setup(){
javax.ws.rs.core.Application app = new javax.ws.rs.core.Application() {
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(JacksonJaxbJsonProvider.class);
return classes;
}
};
//create auth handler
clientConfig = new ClientConfig();
clientConfig.applications(app);
BasicAuthSecurityHandler basicAuthSecurityHandler = new BasicAuthSecurityHandler();
basicAuthSecurityHandler.setUserName(USERNAME);
basicAuthSecurityHandler.setPassword(PASSWORD);
clientConfig.handlers(basicAuthSecurityHandler);
//create client usin auth and provider
client = new RestClient(clientConfig);
}
Then you can post and consume your annotated objects.
#Test
public void aReadWriteTokenCanBeCreatedAsRequested(){
ClientResponse response = client.resource(resourceUrlToken).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).post(readAndWriteToken);
assertEquals("Could not create token needed for test",200,response.getStatusCode());
readAndWriteToken = response.getEntity(TokenResource.class);
assertNotNull("Returned token does not have UUID",readAndWriteToken.getUuid());
}
If you're using maven you can make sure Jackson is on the test classpath (check for compatible versions):
<!-- TEST DEPENDENCIES -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
I wish I could help with registration; but with respect to annotations, I don't think you should need any for Jackson to try to deserialize value. If you are missing something you need you would get different kind of exception.

Categories