Jersey JAXB Based JSON support - json

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>

Related

getContext() method of CustomContextResolver is not called by Jackson

I am struggling with this issue for days now and have no clue how to solve this. Any quick help will be grateful.
I need to convert LocalDate from JSON string which I am receiving from REST service build using apache CXF and jackson. I wrote custom ContextResolver and registered JavaTimeModule in Mapper object.
When I run the application, default constructor is called, that means it has been loaded, but getContext() method which returns ObjectMapper never gets called.
I have registered same ContextResolver in server and client side.
All dependencies are in place(jackson databind, core, annotation, datatype-jsr310).
I am able to fetch JSON response when I hit REST URI directly in browser. Issue comes when I call same URI annotated method from client code
Below is my client code.
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
#Provider //makes this bean a Provider
public class LocalDateObjectMapperContextResolver implements ContextResolver<ObjectMapper>{
private final ObjectMapper MAPPER;
public LocalDateObjectMapperContextResolver() {
MAPPER = new ObjectMapper();
MAPPER.registerModule(new JavaTimeModule());
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return MAPPER;
}
}
<jaxrs:client id="testclient"
serviceClass="package1.RESTService"
username="abc"
password="abc"
address="$serviceURL">
<jaxrs:features>
<bean class="org.apache.cxf.transport.common.gzip.GZIPFeature"/>
<cxf:logging/>
</jaxrs:features>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
<bean class="mypackage.LocalDateObjectMapperContextResolver"/>
</jaxrs:providers>
</jaxrs:client>
Same way, This contextResolver is registered on server side also under
<jaxrs:server>
.....
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
<bean class="mypackage.LocalDateObjectMapperContextResolver"/>
</jaxrs:providers>
</jaxrs:server>
Any reason why getContext is not called?
I also tried by extending ObjectMapper and registering javaTimeModule there, but dont know how to register customObjectMapper in Jackson flow. I just put default constructor for testing, And it does get called while application startup, but then again, No results, I still get same error.
Error: No suitable constructor found for type [simple type, class java.time.LocalDate]: can not instantiate from JSON object (need to add/enable type information?)
I had exactly the same problem #peeskillet describes in question comment.
I was using Jackson dependencies from version 2 and jackson-jaxrs from version 1.
All solved when moved all dependencies to version 2.
If you are using Maven you can add following two maven dependency.
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
And Add following code snippet.
#Configuration
public class CxfConfig {
#Component
#javax.ws.rs.ext.Provider
public static class JacksonJaxbJsonProvider
extends com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider {
#Autowired
private ObjectMapper objectMapper;
#PostConstruct
public void init() {
objectMapper.registerModule(new Jdk8Module());
}
}
}

Spring Data Rest - Configure pagination

Using Spring Data REST with JPA in version 2.1.0.
How can I configure the pagination in order to have the page argument starting at index 1 instead of 0 ?
I have tried setting a custom HateoasPageableHandlerMethodArgumentResolver with an mvc:argument-resolvers, but that doesn't work:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.data.web.HateoasPageableHandlerMethodArgumentResolver">
<property name="oneIndexedParameters" value="true"/>
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
Note that this behaviour is perfectly coherent with the documentation for mvc:argument-resolver that says:
Using this option does not override the built-in support for
resolving handler method arguments. To customize the built-in support
for argument resolution configure RequestMappingHandlerAdapter
directly.
But how can I achieve this ? If possible, in a clean and elegant way ?
The easiest way to do so is to subclass RepositoryRestMvcConfiguration and include your class into your configuration:
class CustomRestMvcConfiguration extends RepositoryRestMvcConfiguration {
#Override
#Bean
public HateoasPageableHandlerMethodArgumentResolver pageableResolver() {
HateoasPageableHandlerMethodArgumentResolver resolver = super.pageableResolver();
resolver.setOneIndexedParameters(true);
return resolver;
}
}
In your XML configuration, replace:
<bean class="….RepositoryRestMvcConfiguration" />
with
<bean class="….CustomRestMvcConfiguration" />
or import the custom class instead of the standard one in your JavaConfig file.
I have configured the RequestMappingHandlerAdapter using a BeanPostProcessor, however I believe that's neither clean, nor elegant. That looks more like a hack. There must be a better way ! I'm giving the code below just for reference.
public class RequestMappingHandlerAdapterCustomizer implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter)bean;
List<HandlerMethodArgumentResolver> customArgumentResolvers = adapter.getCustomArgumentResolvers();
if(customArgumentResolvers != null) {
for(HandlerMethodArgumentResolver customArgumentResolver : customArgumentResolvers) {
if(customArgumentResolver instanceof HateoasPageableHandlerMethodArgumentResolver) {
HateoasPageableHandlerMethodArgumentResolver hateoasPageableHandlerMethodArgumentResolver = (HateoasPageableHandlerMethodArgumentResolver)customArgumentResolver;
hateoasPageableHandlerMethodArgumentResolver.setOneIndexedParameters(true);
}
}
}
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
<beans ...>
<bean class="util.spring.beanpostprocessors.RequestMappingHandlerAdapterCustomizer" />
</beans>
I use to do it using a customizer, which is something that they keep adding for more and more components with every new version:
#Bean
public PageableHandlerMethodArgumentResolverCustomizer pageableResolverCustomizer() {
return resolver -> resolver.setOneIndexedParameters(true);
}
You can put this in any #Configuration class, but ideally you should put it (with any other customization) in one that implements RepositoryRestConfigurer.

Consume a RESTful WS using Spring MVC and Jackson

I would like to consume a RESTful WS using Spring and Jackson.
I'm considering a JSON stream fetched by using Facebook Graph (FC Juventus's JSON data-stream)
This is my controller:
#Controller
public class ConsumeWSController {
#RequestMapping(value = "/consumews", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Consume a RESTful webservice.", locale);
RestTemplate restTemplate = new RestTemplate();
Page page = restTemplate.getForObject("http://graph.facebook.com/juventus", Page.class);
model.addAttribute("pageAbout", page.getAbout());
model.addAttribute("pageAwards", page.getAwards());
return "consumews";
}
}
And the Page class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Page {
private String about;
private String awards;
public String getAbout() {
return about;
}
public void setAbout(String about) {
this.about = about;
}
public String getAwards() {
return awards;
}
public void setAwards(String awards) {
this.awards = awards;
}
}
But the console returns this error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [my.proj.Page] and content type [application/json;charset=UTF-8]
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
How can I fix this error?
Make sure that you have added the correct Jackson package to your classpath. For Jackson 2 and you use Maven:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.3.1</version>
</dependency>
Or if you use the old Jackson add:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.13</version>
</dependency>
You need to define Jackson as your default message converter for JSON content. This is what I do (I use GSON so this might not be the exact syntax for the Jackson message converter):
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</list>
</property>
</bean>
But since you're not defining your RestTemplate as a Spring-managed bean, you need to do it manually:
restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
PS. I see you're using the newer Jackson dependency so the proper mapper might be different in that case.

JAX-RS JAXB JSON Response not working ( MessageBodyProviderNotFoundException)

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.

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