CXF: WARNING: No message body writer has been found for response class ArrayList - cxf

I'm getting the following error:
WARNING: No message body writer has been found for response class ArrayList.
On the following code:
#GET
#Consumes("application/json")
public List getBridges() {
return new ArrayList(bridges);
}
I know it's possible for CXF to handle this case because I've done it before - with a platform that defined the CXF and related maven artifacts behind the scenes (i.e. I didn't know how it was done).
So, the question: how can I get CXF to support this without adding XML bindings or other source code modifications?
Note the following answer addressed the same problem with XML bindings, which is not satisfactory for my case:
No message body writer has been found for response class ArrayList

The problem turns out to be a simple missing Accept header:
Accept: application/json
Adding this to the request resolves the problem.

The best thing is indeed to use Jackson.
The following post gives a great description of why and how to do it.
for your convinience I've summerized the main things:
You will need to set Jackson as the provider, the best way to do it is to use your custom Application overriding javax.ws.rs.core.Application
you will need to add the following code
#Override
public Set<Object> getSingletons() {
Set<Object> s = new HashSet<Object>();
// Register the Jackson provider for JSON
// Make (de)serializer use a subset of JAXB and (afterwards) Jackson annotations
// See http://wiki.fasterxml.com/JacksonJAXBAnnotations for more information
ObjectMapper mapper = new ObjectMapper();
AnnotationIntrospector primary = new JaxbAnnotationIntrospector();
AnnotationIntrospector secondary = new JacksonAnnotationIntrospector();
AnnotationIntrospector pair = new AnnotationIntrospector.Pair(primary, secondary);
mapper.getDeserializationConfig().setAnnotationIntrospector(pair);
mapper.getSerializationConfig().setAnnotationIntrospector(pair);
// Set up the provider
JacksonJaxbJsonProvider jaxbProvider = new JacksonJaxbJsonProvider();
jaxbProvider.setMapper(mapper);
s.add(jaxbProvider);
return s;
}
Finally,
do not forget to add Jackson to your maven
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.7</version>
<scope>compile</scope>
</dependency>

Having the same problem I've finally solved it like this. In your Spring context.xml define bean:
<bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
And use it in the <jaxrs:server> as a provider:
<jaxrs:server id="restService" address="/resource">
<jaxrs:providers>
<ref bean="jsonProvider"/>
</jaxrs:providers>
</jaxrs:server>
In your Maven pom.xml add:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.0</version>
</dependency>

If you are using Jackson you can write custom message body writer.
public class KPMessageBodyWriter implements
MessageBodyWriter<ArrayList<String>> {
private static final Logger LOG = LoggerFactory.getLogger(KPMessageBodyWriter.class);
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return true;
}
public long getSize(ArrayList<String> t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return t.size();
}
public void writeTo(ArrayList<String> t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException,
WebApplicationException {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(entityStream, t);
}
}
In cx configuration file add the provider
<jaxrs:providers>
<bean class="com.kp.KPMessageBodyWriter" />
</jaxrs:providers>

Related

apache camel: custom sftp configuration with sftp component

I am trying to add a custom sftp component in Apache Camel to wrap the username, host, port and password in a configuration object to be passed to a sftpcomponent.
Below is the code that I have tried:
#Configuration
class SftpConfig {
#Bean("sourceSftp")
public SftpComponent getSourceSftpComponent(
#Qualifier("sftpConfig")
SftpConfiguration sftpConfig) throws Exception{
SftpComponent sftpComponent = new SftpComponent();
// not getting way to set the configuration
return sftpComponent;
}
#Bean("sftpConfig")
public SftpConfiguration getSftpConfig(
#Value("${host}") String host,
#Value("${port}") int port,
#Value("${applicationUserName}") String applicationUserName,
#Value("${password}") String password) {
SftpConfiguration sftpConfiguration = new SftpConfiguration();
sftpConfiguration.setHost(host);
sftpConfiguration.setPort(port);
sftpConfiguration.setUsername(applicationUserName);
sftpConfiguration.setPassword(password);
return sftpConfiguration;
}
}
//In other class
from("sourceSftp:<path of directory>") ---custom component
A similar approach in JMSComponent works fine where I have created a bean for sourcejms, but I am not able to do it for sftp as SftpComponent doesn't have set call for sftpconfiguration.
The Camel maintainers seem to be moving away from providing individual components with a "setXXXConfiguration" method to configure their properties. The "approved" method of providing properties -- which works with the SFTP -- is to specify them on the connection URL:
from ("sftp://host:port/foo?username=foo&password=bar")
.to (....)
An alternative approach is to instantiate an endpoint and set its properties, and then use a reference to the endpoint in the from() call. There's a gazillion ways of configuring Camel -- this works for me for XML-based configuration:
<endpoint id="fred" uri="sftp://acme.net/test/">
<property key="username" value="xxxxxxx"/>
<property key="password" value="yyyyyyy"/>
</endpoint>
<route>
<from uri="fred"/>
<to uri="log:foo"/>
</route>
You can customize it by extending the SftpComponent. This allows you to define multiple endpoints without providing the username/password for each endpoint definition.
Step 1: Extend SftpComponent and give your component a custom name, ie customSftp
#Component("customSftp")
public class CustomSftpComponent extends SftpComponent {
private static final Logger LOG = LoggerFactory.getLogger(CustomSftpComponent.class);
#Value("${sftp.username}")
private String username;
#Value("${sftp.password}")
private String password;
#SuppressWarnings("rawtypes")
protected void afterPropertiesSet(GenericFileEndpoint<SftpRemoteFile> endpoint) throws Exception {
SftpConfiguration config = (SftpConfiguration) endpoint.getConfiguration();
config.setUsername(username);
config.setPassword(password);
}
}
Step 2: Create a camel route to poll 2 different folders using your custom component name.
#Component
public class PollSftpRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("{{sftp.endpoint1}}").routeId("pollSftpRoute1")
.log(LoggingLevel.INFO, "Downloaded file from input folder 1.")
.to("file:data/out1");
from("{{sftp.endpoint2}}").routeId("pollSftpRoute2")
.log(LoggingLevel.INFO, "Downloaded file from input folder 2.")
.to("file:data/out2");
}
}
Step 3: Place this in application.properties
camel.springboot.main-run-controller=true
sftp.endpoint1=customSftp://localhost.net/input/1?delay=30s
sftp.endpoint2=customSftp://localhost.net/input/2?delay=30s
sftp.username=sftp_user1_l
sftp.password=xxxxxxxxxxxx
With this you don't have to repeat the username/password for each endpoints.
Note: With this approach you wont be able to set the username/password in URI endpoint configuration. Anything you set in URI will be replaced in afterPropertiesSet.

Application REST Client on Karaf

I'am writing a simple . application deploying on Karaf 4.1.0. It's role is sending a rest request to REST API. When I start my bundle I have an error:
javax.ws.rs.ProcessingException: org.apache.cxf.interceptor.Fault: No message body writer has been found for class package.QueueSharedDTO, ContentType: application/json
at org.apache.cxf.jaxrs.client.WebClient.doResponse(WebClient.java:1149)
at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1094)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)
at org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:428)
at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.method(WebClient.java:1631)
at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.method(WebClient.java:1626)
at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.post(WebClient.java:1566)
at org.apache.cxf.jaxrs.client.spec.InvocationBuilderImpl.post(InvocationBuilderImpl.java:145)
at package.worker.service.implementation.ConnectionServiceImpl.postCheckRequest(ConnectionServiceImpl.java:114)
at package.worker.service.implementation.ConnectionServiceImpl.sendCheck(ConnectionServiceImpl.java:103)
at package.worker.module.QueueSharedListener.run(QueueSharedListener.java:37)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.cxf.interceptor.Fault: No message body writer has been found for class package.QueueSharedDTO, ContentType: application/json
at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1222)
at org.apache.cxf.jaxrs.client.AbstractClient$AbstractBodyWriter.handleMessage(AbstractClient.java:1091)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
at org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:649)
at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1093)
... 11 more
Caused by: javax.ws.rs.ProcessingException: No message body writer has been found for class com.emot.dto.QueueSharedDTO, ContentType: application/json
at org.apache.cxf.jaxrs.client.AbstractClient.reportMessageHandlerProblem(AbstractClient.java:780)
at org.apache.cxf.jaxrs.client.AbstractClient.writeBody(AbstractClient.java:494)
at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1217)
... 15 more
Initialization WebTarget:
private ConnectionServiceImpl() {
client = ClientBuilder.newClient();
client.property(
ClientProperties.CONNECT_TIMEOUT,
snifferProperties.getProperty(SnifferProperties.PARAM_REST_API_CONNECTION_TIMEOUT));
client.property(
ClientProperties.READ_TIMEOUT,
snifferProperties.getProperty(SnifferProperties.PARAM_REST_API_READ_TIMEOUT));
System.out.println(2);
webTarget = client.target(buildUrl());
}
Send requests :
private synchronized boolean postCheckRequest(String path, Object content) {
boolean result = true;
try {
Response response = webTarget
.path("check")
.path("add/one")
.request(MediaType.APPLICATION_JSON)
.post(Entity.json(content));
result = (response.getStatus() == 200);
} catch (Exception e) {
System.out.println("Error but working");
e.printStackTrace();
result = false;
}
return result;
}
I have always the problems with Karaf... i dont understand why it . couldn't working correctly...
The issue you are facing is mostly not a Karaf issue, but a typical issue you may face while working with some JAX-RS implementation in non-JavaEE environment.
Exception literally says that your implementation misses message body writer. Message body writer is the class which implements class javax.ws.rs.ext.MessageBodyWriter and is responsible for serializing your data objects to some format (like JSON). There is another class named javax.ws.rs.ext.MessageBodyReader, which does the opposite thing. All these classes are registered to JAX-RS framework as providers, extending its capabilities. Details are here: https://jersey.java.net/documentation/latest/message-body-workers.html
So, generally you must decide what you use for serializing/deserializing between your data objects and HTTP MediaType and register a proper JAX-RS provider.
With Jackson, for example, your problem can be easily solved by using one of its standard implementation: either com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider, if you use JAXB annotations, or com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider, if you prefer Jackson annotations. Add this class in providers section of your Blueprint descriptor:
<jaxrs:server id="restServer" address="/rest">
<jaxrs:serviceBeans>
....
</jaxrs:serviceBeans>
<jaxrs:providers>
....
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>
....
</jaxrs:providers>
</jaxrs:server>

Configuring Jetty component in Camel 2.15

I'm trying to start using Jetty with Camel.
I have added the dependency to my pom:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jetty</artifactId>
<version>2.15.5</version>
</dependency>
My CamelContext is initialized as follows:
public void startCamelContext() throws Exception {
CamelContext camelContext = new DefaultCamelContext();
camelContext.addComponent("jetty", new JettyHttpComponent8());
camelContext.start();
}
When I try to start up my service, which has a route with endpoint defined as:
jetty:http://0.0.0.0:9000/httpInput
I get an exception:
java.lang.NullPointerException: null
at org.apache.camel.component.jetty8.JettyHttpComponent8.createConnectorJettyInternal(JettyHttpComponent8.java:48)
at org.apache.camel.component.jetty.JettyHttpComponent.createConnector(JettyHttpComponent.java:585)
at org.apache.camel.component.jetty.JettyHttpComponent.getSocketConnector(JettyHttpComponent.java:527)
at org.apache.camel.component.jetty.JettyHttpComponent.getConnector(JettyHttpComponent.java:517)
at org.apache.camel.component.jetty.JettyHttpComponent.connect(JettyHttpComponent.java:320)
at org.apache.camel.component.http.HttpEndpoint.connect(HttpEndpoint.java:185)
at org.apache.camel.component.http.HttpConsumer.doStart(HttpConsumer.java:53)
at org.apache.camel.support.ServiceSupport.start(ServiceSupport.java:61)
at org.apache.camel.impl.DefaultCamelContext.startService(DefaultCamelContext.java:2885)
at org.apache.camel.impl.DefaultCamelContext.doStartOrResumeRouteConsumers(DefaultCamelContext.java:3179)
at org.apache.camel.impl.DefaultCamelContext.doStartRouteConsumers(DefaultCamelContext.java:3115)
at org.apache.camel.impl.DefaultCamelContext.safelyStartRouteServices(DefaultCamelContext.java:3045)
at org.apache.camel.impl.DefaultCamelContext.doStartOrResumeRoutes(DefaultCamelContext.java:2813)
at org.apache.camel.impl.DefaultCamelContext.startAllRoutes(DefaultCamelContext.java:865)
The documentation on how to set up the Jetty component is lacking at best. I found a mailing-list entry where it was said that JettyHttpComponent has been made abstract since Camel 2.15 and now that component has to be configured using JettyHttpComponent8 or 9. link
In my case, I'm using Camel 2.15.5 and the JettyHttpComponent9 isn't available in the classpath, and using 8 gives the exception described above.
I also found related discussion here with no information on how to actually use that component.
That's typically not how the CamelContext is initialized/started. Please consider using an archetype to get started, then add the Jetty Maven dependency and see if the error can be reproduced.
Camel archetypes can be found here: http://camel.apache.org/camel-maven-archetypes.html
To start a camel context outside spring you need to create a continuous thread to keep camel alive, as explaine here: http://camel.apache.org/running-camel-standalone-and-have-it-keep-running.html
Don't worry, I have below some code that will setup a jetty on localhost:8081 for you:
pom.xml
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jetty</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>2.16.1</version>
</dependency>
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.main.Main;
import org.apache.camel.main.MainListenerSupport;
import org.apache.camel.main.MainSupport;
import java.util.Date;
/**
* Created by mkbrv on 22/06/16.
*/
public class CamelJetty {
private Main main;
public static void main(String[] args) throws Exception {
CamelJetty example = new CamelJetty();
example.boot();
}
public void boot() throws Exception {
// create a Main instance
main = new Main();
// bind MyBean into the registry
main.bind("foo", new MyBean());
// add routes
main.addRouteBuilder(new MyJettyRouteBuilder());
// add event listener
main.addMainListener(new Events());
// run until you terminate the JVM
System.out.println("Starting Camel. Use ctrl + c to terminate the JVM.\n");
main.run();
}
private static class MyJettyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("jetty:http://localhost:8081")
.process(exchange -> {
System.out.println("Invoked timer at " + new Date());
exchange.getOut().setBody("Hi, this is Camel!");
})
.bean("foo");
}
}
public static class MyBean {
public void callMe() {
System.out.println("MyBean.callMe method has been called");
}
}
public static class Events extends MainListenerSupport {
#Override
public void afterStart(MainSupport main) {
System.out.println("MainExample with Camel is now started!");
}
#Override
public void beforeStop(MainSupport main) {
System.out.println("MainExample with Camel is now being stopped!");
}
}
}
Next just go to http://localhost:8081 and you should see a welcome message.
Have fun tweaking this further more.

Spring + AngularJS - the server responded with a status of 406 (Not Acceptable) [duplicate]

this is my javascript:
function getWeather() {
$.getJSON('getTemperature/' + $('.data option:selected').val(), null, function(data) {
alert('Success');
});
}
this is my controller:
#RequestMapping(value="/getTemperature/{id}", headers="Accept=*/*", method = RequestMethod.GET)
#ResponseBody
public Weather getTemparature(#PathVariable("id") Integer id){
Weather weather = weatherService.getCurrentWeather(id);
return weather;
}
spring-servlet.xml
<context:annotation-config />
<tx:annotation-driven />
Getting this error:
GET http://localhost:8080/web/getTemperature/2 406 (Not Acceptable)
Headers:
Response Headers
Server Apache-Coyote/1.1
Content-Type text/html;charset=utf-8
Content-Length 1070
Date Sun, 18 Sep 2011 17:00:35 GMT
Request Headers
Host localhost:8080
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0.2) Gecko/20100101 Firefox/6.0.2
Accept application/json, text/javascript, */*; q=0.01
Accept-Language en-us,en;q=0.5
Accept-Encoding gzip, deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection keep-alive
X-Requested-With XMLHttpRequest
Referer http://localhost:8080/web/weather
Cookie JSESSIONID=7D27FAC18050ED84B58DAFB0A51CB7E4
Interesting note:
I get 406 error, but the hibernate query works meanwhile.
This is what tomcat log says, everytime when I change selection in dropbox:
select weather0_.ID as ID0_0_, weather0_.CITY_ID as CITY2_0_0_, weather0_.DATE as DATE0_0_, weather0_.TEMP as TEMP0_0_ from WEATHER weather0_ where weather0_.ID=?
What could the problem be? There were two similar questions in SO before, I tried all the accepted hints there, but they did not work I guess...
Any suggestions? Feel free to ask questions...
406 Not Acceptable
The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.
So, your request accept header is application/json and your controller is not able to return that. This happens when the correct HTTPMessageConverter can not be found to satisfy the #ResponseBody annotated return value. HTTPMessageConverter are automatically registered when you use the <mvc:annotation-driven>, given certain 3-d party libraries in the classpath.
Either you don't have the correct Jackson library in your classpath, or you haven't used the
<mvc:annotation-driven> directive.
I successfully replicated your scenario and it worked fine using these two libraries and no headers="Accept=*/*" directive.
jackson-core-asl-1.7.4.jar
jackson-mapper-asl-1.7.4.jar
I had same issue, with Latest Spring 4.1.1 onwards you need to add following jars to pom.xml.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1.1</version>
</dependency>
also make sure you have following jar:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
406 Spring MVC Json, not acceptable according to the request "accept" headers
There is another case where this status will be returned: if the Jackson mapper cannot figure out how to serialize your bean. For example, if you have two accessor methods for the same boolean property, isFoo() and getFoo().
What's happening is that Spring's MappingJackson2HttpMessageConverter calls Jackson's StdSerializerProvider to see if it can convert your object. At the bottom of the call chain, StdSerializerProvider._createAndCacheUntypedSerializer throws a JsonMappingException with an informative message. However, this exception is swallowed by StdSerializerProvider._createAndCacheUntypedSerializer, which tells Spring that it can't convert the object. Having run out of converters, Spring reports that it's not being given an Accept header that it can use, which of course is bogus when you're giving it */*.
There is a bug for this behavior, but it was closed as "cannot reproduce": the method that's being called doesn't declare that it can throw, so swallowing exceptions is apparently an appropriate solution (yes, that was sarcasm). Unfortunately, Jackson doesn't have any logging ... and there are a lot of comments in the codebase wishing it did, so I suspect this isn't the only hidden gotcha.
I had the same problem, my controller method executes but response is Error 406.
I debug AbstractMessageConverterMethodProcessor#writeWithMessageConverters and found that method ContentNegotiationManager#resolveMediaTypes always returns text/html which is not supported by MappingJacksonHttpMessageConverter. The problem is that the org.springframework.web.accept.ServletPathExtensionContentNegotiationStrategy works earlier than org.springframework.web.accept.HeaderContentNegotiationStrategy, and extension of my request /get-clients.html is the cause of my problem with Error 406. I just changed request url to /get-clients.
Make sure that the sent object (Weather in this case) contains getter/setter
Make sure that following 2 jar's are present in class path.
If any one or both are missing then this error will come.
jackson-core-asl-1.9.X.jar jackson-mapper-asl-1.9.X.jar
Finally found answer from here:
Mapping restful ajax requests to spring
I quote:
#RequestBody/#ResponseBody annotations don't use normal view resolvers, they use their own HttpMessageConverters. In order to use these annotations, you should configure these converters in AnnotationMethodHandlerAdapter, as described in the reference (you probably need MappingJacksonHttpMessageConverter).
Check <mvc:annotation-driven /> in dispatcherservlet.xml , if not add it.
And add
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
these dependencies in your pom.xml
Probably no one is scrolling down this far, but none of the above solutions fixed it for me, but making all my getter methods public did.
I'd left my getter visibility at package-private; Jackson decided it couldn't find them and blew up. (Using #JsonAutoDetect(getterVisibility=NON_PRIVATE) only partially fixed it.
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-base</artifactId>
<version>2.6.3</version>
</dependency>
I was having the same problem because I was missing the #EnableWebMvc annotation. (All of my spring configurations are annotation-based, the XML equivalent would be mvc:annotation-driven)
In the controller, shouldn't the response body annotation be on the return type and not the method, like so :
#RequestMapping(value="/getTemperature/{id}", headers="Accept=*/*", method = RequestMethod.GET)
public #ResponseBody Weather getTemparature(#PathVariable("id") Integer id){
Weather weather = weatherService.getCurrentWeather(id);
return weather;
}
I'd also use the raw jquery.ajax function, and make sure contentType and dataType are being set correctly.
On a different note, I find the spring handling of json rather problematic. It was easier when I did it all myself using strings, and GSON.
As #atott mentioned.
If you have added the latest version of Jackson in your pom.xml, and with Spring 4.0 or newer, using #ResponseBody on your action method and #RequestMapping configured with produces="application/json;charset=utf-8", however, you still got 406(Not Acceptable), I guess you need to try this in your MVC DispatcherServlet context configuration:
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
</bean>
That's the way how I resolved my issue finally.
check this thread.
spring mvc restcontroller return json string
p/s: you should add jack son mapping config to your WebMvcConfig class
#Override
protected void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
// put the jackson converter to the front of the list so that application/json content-type strings will be treated as JSON
converters.add(new MappingJackson2HttpMessageConverter());
// and probably needs a string converter too for text/plain content-type strings to be properly handled
converters.add(new StringHttpMessageConverter());
}
Spring 4.3.10: I used the below settings to resolve the issue.
Step 1: Add the below dependencies
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
Step 2: Add the below in your MVC DispatcherServlet context configuration:
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false"/>
<property name="favorParameter" value="true"/>
<property name="ignoreAcceptHeader" value="false" />
</bean>
Since spring 3.2, as per the default configuration favorPathExtension is set as true, because of this if the request uri have any proper extensions like .htm spring will give priority for the extension. In step 2 I had added the contentNegotiationManager bean to override this.
make sure your have correct jackson version in your classpath
Check as #joyfun did for the correct version of jackson but also check our headers ... Accept / may not be transmitted by the client ... use firebug or equivalent to check what your get request is actually sending. I think the headers attribute of the annotation /may/ be checking literals although I'm not 100% sure.
Other then the obvious problems I had another one that I couldn't fix regardless of including all possible JARs, dependancies and annotations in Spring servlet. Eventually I found that I have wrong file extension by that I mean I had two separate servlet running in same container and I needed to map to different file extensions where one was ".do" and the other as used for subscriptions was randomly named ".sub". All good but SUB is valid file extension normally used for films subtitle files and thus Tomcat was overriding the header and returning something like "text/x-dvd.sub..." so all was fine but the application was expecting JSON but getting Subtitles thus all I had to do is change the mapping in my web.xml file I've added:
<mime-mapping>
<extension>sub</extension>
<mime-type>application/json</mime-type>
</mime-mapping>
I had the same problem unfortunately non of the solution here solved my problem as my problem was something in a different class.
I first checked that all dependencies are in place as suggested by #bekur
then I checked the request/response that travels from clients to the server all headers was in place an properly set by Jquery.
I then checked the RequestMappingHandlerAdapter MessageConverters and all 7 of them were in place, I really started to hate Spring ! I then updated to from Spring 4.0.6.RELEASE to 4.2.0.RELEASE I have got another response rather than the above. It was Request processing failed; nested exception is java.lang.IllegalArgumentException: No converter found for return value of type
Here is my controller method
#RequestMapping(value = "/upload", method = RequestMethod.POST,produces = "application/json")
public ResponseEntity<UploadPictureResult> pictureUpload(FirewalledRequest initialRequest) {
DefaultMultipartHttpServletRequest request = (DefaultMultipartHttpServletRequest) initialRequest.getRequest();
try {
Iterator<String> iterator = request.getFileNames();
while (iterator.hasNext()) {
MultipartFile file = request.getFile(iterator.next());
session.save(toImage(file));
}
} catch (Exception e) {
return new ResponseEntity<UploadPictureResult>(new UploadPictureResult(),HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<UploadPictureResult>(new UploadPictureResult(), HttpStatus.OK);
}
public class UploadPictureResult extends WebResponse{
private List<Image> images;
public void setImages(List<Image> images) {
this.images = images;
}
}
public class WebResponse implements Serializable {
protected String message;
public WebResponse() {
}
public WebResponse(String message) {
this.message = message;
}
public void setMessage(String message) {
this.message = message;
}
}
The solution was to make UploadPictureResult not to extend WebResponse
For some reason spring was not able to determine the how to convert UploadPictureReslt when it extended WebResponse
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.0</version>
</dependency>
i don't use ssl authentication and this jackson-databind contain jackson-core.jar and jackson-databind.jar, and then change the RequestMapping content like this:
#RequestMapping(value = "/id/{number}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
public #ResponseBody Customer findCustomer(#PathVariable int number){
Customer result = customerService.findById(number);
return result;
}
attention:
if your produces is not "application/json" type and i had not noticed this and got an 406 error, help this can help you out.
This is update answer for springVersion=5.0.3.RELEASE.
Those above answers will be only worked older springVersion < 4.1 version. for latest spring you have to add following dependencies in gradle file:
compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: fasterxmljackson
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: fasterxmljackson
fasterxmljackson=2.9.4
I hope this will be helpful for who using latest spring version.
Simple answer just add Getter method in your domain/model class.
But Why this works ??
Under the hood Spring used HttpMessageConverters to convert your input JSON to Java Object. The Accept header that is passed in the request is used to select appropriate MessageConvertor at runtime. These message convertors use getter of your domain/model class for conversion, so if there are no getter method, Marshall and unmarshall Java Objects to and from JSON will not happen, even if you add Jackson in your classpath, because even Jackson lib uses Getter methods for marshalling stuffs !!.
Can you remove the headers element in #RequestMapping and try..
Like
#RequestMapping(value="/getTemperature/{id}", method = RequestMethod.GET)
I guess spring does an 'contains check' rather than exact match for accept headers. But still, worth a try to remove the headers element and check.

CXF custom validation and FaultOutInterceptors for manage Errors

I have developed a web service using cxf. In case of an error due to a request that doesn't respect the xsd schema asociated I would custom the error sent to the client. For that:
1- I have added a specific EventHandlerValidator and a specific FaultOutInterceptor in web-services.xml
<jaxws:endpoint id="getNewCustomerOrderId" implementor="#getNewCustomerOrderIdWS" address="/GetNewCustomerOrderId">
<jaxws:properties>
<entry key="jaxb-validation-event-handler">
<ref bean="getNewCustomerOrderIdEventHandlerValidator"/>
</entry>
<entry key="schema-validation-enabled" value="IN"/>
<entry key="set-jaxb-validation-event-handler" value="true"/>
</jaxws:properties>
<jaxws:outFaultInterceptors>
<ref bean="getNewCustomerOrderIdCXFFaultOutInterceptor"/>
</jaxws:outFaultInterceptors>
</jaxws:endpoint>`enter code here
2 - I have implemented these classes:
In the handleValidator I just throw my own exception with a code and message
public class GetNewCustomerOrderIdEventHandlerValidator implements ValidationEventHandler {
#Override
public boolean handleEvent(ValidationEvent event) {
throw new MyException(MyExceptionCode.ERCC_GNCOI_100, event.getMessage());
}
}
FaultExceptionInterceptor runs for every exception thrown during webservice call. I only want to catch MyException with code ERCC_GNCOI_100 for customizing it, so:
public class GetNewCustomerOrderIdCXFFaultOutInterceptor extends AbstractSoapInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(CreateCustomerOrderCXFFaultOutInterceptor.class);
#Inject
private CreateCustomerOrderFaultExceptionService createCustomerOrderFaultExceptionService;
private static final JAXBContext jaxbContext;
static {
try {
jaxbContext = JAXBContext.newInstance(CreateCustomerOrderException.class);
} catch (JAXBException e) {
LOGGER.error(CormoranMarker.TECH, "Error during JAXBContext instantiation");
throw new RuntimeException(e);
}
}
public GetNewCustomerOrderIdCXFFaultOutInterceptor() {
super(Phase.MARSHAL);
}
#Override
public void handleMessage(SoapMessage message) throws Fault {
Fault exceptionFault = (Fault) message.getContent(Exception.class);
exceptionFault.setMessage("My custom message");
if (exceptionFault.getCause() instanceof MyException) {
MyException myException = (MyException) exceptionFault
.getCause();
if (myException.getCode().equals(myException.ERCC_GNCOI_100)) {// validation
// schema
// errors
Element elt = buildExceptionFaultDetail(cormoranFunctionalException);
exceptionFault.setDetail(elt);
}
}
}
private Element buildExceptionFaultDetail(CormoranFunctionalException cormoranFunctionalException) {
// Build custom response
}
}
However, in the interceptor I'm not able to catch my exception:
Fault exceptionFault = (Fault) message.getContent(Exception.class);
This line gets an unmarshalling exception:
Unmarshalling Error: cvc-complex-type.2.4.a: Invalid content was found starting with element 'customerOrderType1'. One of '{customerOrderID, version, customerOrderType, depositDate}' is expected.
In the logs I see that My exception has been thrown:
12:32:27.338 [qtp200426125-38] ERROR c.o.c.c.e.MyException - [] - MyException : Non-respect du schéma (XSD) du WebService exposé par Cormoran : cvc-complex-type.2.4.a: Invalid content was found starting with element 'customerOrderType1'. One of '{customerOrderID, version, customerOrderType, depositDate}' is expected.
Could you help me?
Thank you in advance!
Auri
There are two problems with the Interceptor as written.
First, you need to set the new content to the message after you make your changes. To do that, you can add the following to the handleMessage method after your code
message.setContent(Exception.class, exceptionFault);
Second, the phase you chose was too late to make changes to the Fault object. It looks like PRE_STREAM is the latest phase that allows the change. The CXF Interceptor documentation has the full list of phases.
You can modify the message as below.
Fault exceptionFault = (Fault) message..getExchange().getContent(Exception.class);

Resources