CXF #PUT request "No message body writer has been found" exception - spring

I am completely stuck! spends many hours on this with no progress...
I have a Spring 4 (4.2.3.RELEASE) app with CXF 3 (3.1.4) which I am trying to JUnit test. Everything is working great except for the PUT request. I am getting the following error:
Caused by: org.apache.cxf.interceptor.Fault: No message body writer has been found for class com.someproject.logic.api.data.User, ContentType: application/xml
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)
... 49 more
Caused by: javax.ws.rs.ProcessingException: No message body writer has been found for class com.someproject.logic.api.data.User, ContentType: application/xml
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)
... 53 more
I also tried with "application/json" and got the same result.
Here is the CXF configuration:
#Bean
#DependsOn("cxf")
public Server jaxRsServer() {
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
List<Object> providers = new ArrayList<Object>();
// get all the class annotated with #JaxrsService
List<Object> beans = configUtil.findBeans(JaxrsService.class);
if (beans.size() > 0) {
// add all the CXF service classes into the CXF stack
sf.setServiceBeans(beans);
sf.setAddress("http://localhost:8080/api");
sf.setBus(springBus);
sf.setStart(true);
// set JSON as the response serializer
JacksonJsonProvider provider = new JacksonJsonProvider();
providers.add( provider );
}
// add custom providers if any
sf.setProviders(providers);
return sf.create();
}
Endpoint:
#Path("/user")
#PUT
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, })
#Produces( MediaType.APPLICATION_JSON )
public User updateUser( User user );
Endpoint impl:
#Override
public User updateUser( User user ) {
System.out.println( "updateUser: " + user );
return user;
}
Test:
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
webClient = WebClient.create( "http://localhost:8080/api" );
WebClient.getConfig( webClient ).getRequestContext().put( LocalConduit.DIRECT_DISPATCH, Boolean.TRUE );
webClient.accept( "application/json" );
}
#Test
public void testPut() {
String apiUrl = "/user";
webClient.path( apiUrl );
User user = createDummyAPIUser();
try {
System.out.println( "testUpdateUser: PUT request to " + apiUrl );
String response = webClient.type( MediaType.APPLICATION_JSON ).put( user, String.class );
System.out.println( "testUpdateUser: " + apiUrl + " response: " + response );
//updatedUser = (new ObjectMapper()).readValue( response, User.class );
} catch( Exception e ) {
e.printStackTrace();
fail();
return;
}
}
First how do I make sure that the fasterxml Jackson 2 provider is in fact serializing the message body? It do not see anything Jackson in the stacktrace but I do set it in the providers.
I have found this link to demonstrate a custom ContextProvider, is this the only way to get this working? Seem utterly redundant...
http://www.blackpepper.co.uk/custom-context-providers-for-cxf-with-the-context-annotation/
Any ideas?
Thank you!!

GEEEEEEZZZ...
feeling like a dork, forgot to add the same Jackson serializer ( provider ) to the client. Looking again through the stacktrace I noticed that the methods were only from the client, so obviously the client did not know how to consume the POJO I was throwing at it...
Updated test code:
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
final List<Object> providers = new ArrayList<Object>();
JacksonJaxbJsonProvider jacksonJsonProvider = new JacksonJaxbJsonProvider();
providers.add( jacksonJsonProvider );
webClient = WebClient.create( "http://localhost:8080/api", providers );
WebClient.getConfig( webClient ).getRequestContext().put( LocalConduit.DIRECT_DISPATCH, Boolean.TRUE );
webClient.accept( "application/json" );
}

Related

How to mock third party API calls?

I have a spring boot application that has the below AuthFilter added for all rest apis exposed by the application. I want to test the below code that validates authorization token by calling a third party api call. I tried Mockito but how do I inject the mocked HttpPost, HttpClient etc object in the filter class?
Also what value do I pass to thirdPartyAPIUrl property which is configured in application.properties for test class
#Component
public class AuthTokenFilter implements Filter {
public boolean isAuthTokenValid(HttpServletRequest request, HttpServletResponse response) throws IOException {
String authorizationToken = request.getHeader(RequestHeaders.AUTHORIZATION.toString());
TokenRequest validateTokenRequest = new TokenRequest();
validateTokenRequest.setToken(authorizationToken);
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(this.thirdPartyAPIUrl); //fetched through application.properties
httpPost.setHeader("Content-type", "application/json");
StringEntity requestBody = new StringEntity(new Gson().toJson(validateTokenRequest));
httpPost.setEntity(requestBody);
try (CloseableHttpResponse validateTokenResponse = httpclient.execute(httpPost)) {
HttpEntity rEntity = validateTokenResponse.getEntity();
TokenResponse tokenResponse = new ObjectMapper().readValue(rEntity.getContent(),
TokenResponse.class);
logger.debug("API Response Object : {}", tokenResponse);
}
}
return false; //temporary
}
}
Thanks!
I would recommend avoiding mocking HttpPost etc and instead just mocking the third-party server. My preferred tool to use for this is wiremock
Here is an example of how it would be used:
(make sure to import this for options, caused me a lot of headaches ;) )
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
... code
static WireMockServer wireMockServer = new WireMockServer(options().port(8080));
#BeforeAll
static void init() {
wireMockServer.start();
}
//this is for the case that you have multiple test suites that mock the server, to avoid conflicts with ports
#AfterAll
static void releaseResource() {
wireMockServer.stop();
}
#Test
void test() {
wireMockServer.stubFor(post("/endpoint").willReturn(aResponse().withStatus(200)));
... more code
filter.isAuthTokenValid(request, response);
}

Testing a Post multipart/form-data request on REST Controller

I've written a typical spring boot application, now I want to add integration tests to that application.
I've got the following controller and test:
Controller:
#RestController
public class PictureController {
#RequestMapping(value = "/uploadpicture", method = RequestMethod.POST)
public ResponseEntity<VehicleRegistrationData> uploadPicturePost(#RequestPart("userId") String userId, #RequestPart("file") MultipartFile file) {
try {
return ResponseEntity.ok(sPicture.saveAndParsePicture(userId, file));
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
Test:
#Test
public void authorizedGetRequest() throws Exception {
File data = ResourceUtils.getFile(testImageResource);
byte[] bytes = FileUtils.readFileToByteArray(data);
ObjectMapper objectMapper = new ObjectMapper();
MockMultipartFile file = new MockMultipartFile("file", "test.jpg", MediaType.IMAGE_JPEG_VALUE, bytes);
MockMultipartFile userId =
new MockMultipartFile("userId",
"userId",
MediaType.MULTIPART_FORM_DATA_VALUE,
objectMapper.writeValueAsString("123456").getBytes()
);
this.mockMvc.perform(multipart("/uploadPicture")
.file(userId)
.file(file)
.header(API_KEY_HEADER, API_KEY)).andExpect(status().isOk());
}
Testing the controller with the OkHttp3 client on android works seamlessly, but I can't figure out how to make that request work on the MockMvc
I expect 200 as a status code, but get 404 since, I guess, the format is not the correct one for that controller
What am I doing wrong?
It must be a typo.
In your controller, you claim the request URL to be /uploadpicture, but you visit /uploadPicture for unit test.

Spring Boot Camel Route - get data from rest endpoint

I want to create camel route in Spring Boot (2.1.1) project to get the data from some (rest) endpoint (http://localhost:8080/getAllUsers) and to send that data to activeMq.
I have tried with timer data to send it on activeMq and to consume it and it is working. But I have problem with collecting data from endpoint.
I have tried several things but no success. This is what I have tried.
In this example I am not sending the data to ActiveMq, I just want to see the response...
public void createNewRoute() {
CamelContext context = new DefaultCamelContext();
try {
ProducerTemplate template = context.createProducerTemplate();
context.start();
Exchange exchange = template.request("http://localhost:8080/getAllUsers",
new Processor() {
public void process(Exchange exchange) throws Exception {
}
});
if (null != exchange) {
Message out = exchange.getOut();
int responseCode = out.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
System.out.println("Response: " + String.valueOf(responseCode));
}
Thread.sleep(1000 * 3);
context.stop();
} catch (Exception ex) {
System.out.println("Exception: " + ex);
}
System.out.println("DONE!!");
}
Another route:
from("servlet://localhost:8080/getAllUsers").to("activemq://all-users");
And another:
rest("//localhost:8080/getAllUsers")
.get().consumes("application/json")
.to("activemq://all-users");
I will go with your second example:
from("timer://test?repeatCount=1").routeId("newRoute")
.streamCaching()
.process(exchange -> exchange.getIn()
.setBody(exchange.getIn()
.getBody()))
.marshal()
.json(JsonLibrary.Jackson)
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.to("http://localhost:8080/getAllUsers")
.log(LoggingLevel.INFO, "This is my body: ${body}")
.to("activemq:queue://new-queue");
This will trigger it once.
Try this without context.start() ....
CamelContext camelContext = new DefaultCamelContext();
ProducerTemplate template = camelContext.createProducerTemplate();
Exchange exchange = template.send("http://localhost:8080/getAllUsers", new Processor() {
public void process(Exchange exchange) throws Exception {}
});
Message out = exchange.getOut();
The http components are streaming based, so you can ask Camel to give you the response as string instead.
String s = exchange.getMessage().getBody(String.class);
See more in these links
http://camel.apache.org/stream-caching
http://camel.apache.org/why-is-my-message-body-empty.html

Retrofit returns content length 0 with spring data rest

I am using spring boot application with spring data rest deployed on heroku. I have a /api/userDatas end point on which an entity can be created by a POST request. I have tested it using Postman and it gets created.
Now I am using retrofit on android to perform the same functionality. But the problem is that entity gets created on the server but onFailure() always gets called. I have debugged and found that content-length is always 0.
CreateUser
private void createUser() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(logging).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ServeNetworking.ServeURLS.getBaseURL())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
ServeNetworking serveNetworking = retrofit.create(ServeNetworking.class);
Call<UserData> getUserByIdResponseCall = serveNetworking.socialConnectAPI(userData);
getUserByIdResponseCall.enqueue(new Callback<UserData>() {
#Override
public void onResponse(Call<UserData> call, Response<UserData> response) {
Log.d("getUserById", "onResponse");
Toast.makeText(OTPVerificationActivity.this, "userId : onResponse", Toast.LENGTH_LONG).show();
/**
* Save data in local db and navigate to map screen.
*/
ActivityAnimationUtils.presentActivity(OTPVerificationActivity.this, MapActivity.class);
try{
DBManager.getDBManager(OTPVerificationActivity.this).setIsVerified(true);
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onFailure(Call<UserData> call, Throwable t) {
Log.d("getUserById", "onFailure");
Utils.showSnackBar(OTPVerificationActivity.this,"Somethign went wrong", root);
}
});
}
and interface:
public interface ServeNetworking {
#POST("/api/userDatas")
#Headers({ "Content-Type: application/json;charset=UTF-8"})
Call<UserData> socialConnectAPI(
#Body UserData user
);
#GET("/api/userDatas/{userId}")
#Headers({ "Content-Type: application/json; charset=utf-8"})
Call<UserData> getUser(
#Path("userId") String userId
);
/**
* this class is used to get the donor base urls...
*/
class ServeURLS{
private static String baseURL="https://fabrimizer-serve.herokuapp.com";
public static String getBaseURL() {
return baseURL;
}
}
}
I am getting following error:
java.io.EOFException: End of input at line 1 column 1 path $
Found the solution.
added Accept header in ServeNetworking:
#POST("/api/userDatas")
#Headers({ "Content-Type: application/json,"Accept: application/json"})
Call<UserData> socialConnectAPI(
#Body UserData user
);

Resolving views in Spring MVC with Freemarker - including jsp page

When using the FreemarkerServlet, it is possible to include JSP pages alongside Freemarker content. However, I'm using Freemarker as a view resolver in my Spring MVC application and so don't use the FreemarkerServlet. Is there any way I can still use #include_page in my Freemarker templates when I'm not using the FreemarkerServlet?
Thanks,
Andrew
I found the solution. You need to sub-class the FreemarkerView to add the include behaviour:
public class CustomFreeMarkerView extends FreeMarkerView
{
private TaglibFactory taglibFactory;
private ServletContextHashModel servletContextHashModel;
#Override
protected void doRender( Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response )
throws Exception
{
// Expose model to JSP tags (as request attributes).
exposeModelAsRequestAttributes( model, request );
// Expose all standard FreeMarker hash models.
model.put( FreemarkerServlet.KEY_JSP_TAGLIBS, this.taglibFactory );
model.put( FreemarkerServlet.KEY_APPLICATION,
this.servletContextHashModel );
model.put( FreemarkerServlet.KEY_SESSION, buildSessionModel( request,
response ) );
model.put( FreemarkerServlet.KEY_REQUEST, new HttpRequestHashModel(
request, response, getObjectWrapper() ) );
model.put( FreemarkerServlet.KEY_REQUEST_PARAMETERS,
new HttpRequestParametersHashModel( request ) );
model.put( FreemarkerServlet.KEY_INCLUDE, new IncludePage( request,
response ) );
if ( logger.isDebugEnabled() )
{
logger.debug( "Rendering FreeMarker template [" + getUrl()
+ "] in FreeMarkerView '" + getBeanName() + "'" );
}
// Grab the locale-specific version of the template.
Locale locale = RequestContextUtils.getLocale( request );
processTemplate( getTemplate( locale ), new SimpleHash( model ), response );
}
private HttpSessionHashModel buildSessionModel( HttpServletRequest request,
HttpServletResponse response )
{
HttpSession session = request.getSession( false );
if ( session != null )
{
return new HttpSessionHashModel( session, getObjectWrapper() );
}
else
{
return new HttpSessionHashModel( null, request, response,
getObjectWrapper() );
}
}
}
The key line:
model.put( FreemarkerServlet.KEY_INCLUDE, new IncludePage( request,
response ) );
and then sub-class the view resolver to utilise the custom view:
public class CustomFreeMarkerViewResolver extends FreeMarkerViewResolver
{
public CustomFreeMarkerViewResolver()
{
setViewClass( requiredViewClass() );
}
/**
* Requires {#link FreeMarkerView}.
*/
#Override
protected Class requiredViewClass()
{
return CustomFreeMarkerView.class;
}
}
This post was very helpful. I'm not sure if this will help, but here's my solution:
Here's my doRender method:
#Override
protected void doRender( Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response )
throws Exception
{
//Expose model to JSP tags (as request attributes).
exposeModelAsRequestAttributes(model, request);
// Expose all standard FreeMarker hash models.
SimpleHash fmModel = buildTemplateModel(model, request, response);
// add the include_page directive
fmModel.put( FreemarkerServlet.KEY_INCLUDE, new IncludePage( request, response));
if (logger.isDebugEnabled()) {
logger.debug("Rendering FreeMarker template [" + getUrl() + "] in FreeMarkerView '" + getBeanName() + "'");
}
// Grab the locale-specific version of the template.
Locale locale = RequestContextUtils.getLocale( request );
processTemplate(getTemplate(locale), fmModel, response);
}
drewzilla's answer is almost worked for me. but i had to use
getTemplate(locale).process(model, response.getWriter());
instead of
processTemplate( getTemplate( locale ), new SimpleHash( model ), response );
with spring

Resources