Camel asynchronous call get the response - apache-camel

I am making an asynchronous call to a webservice using apache camel API using below code:
Exchange exchange = new DefaultExchange(context);
Message msg = exchange.getIn();
msg.setBody(requestStr);
msg.setHeader("content-type", "application/json");
template.asyncCallback("direct:invokeAPI", exchange, new Synchronization() {
#Override
public void onComplete(Exchange exchange) {
System.out.println("Success");
System.out.println(exchange);
System.out.println(exchange.getIn());
System.out.println(exchange.getIn().getHeaders());
System.out.println(exchange.getIn().getBody());
System.out.println("================================");
System.out.println(exchange.getOut());
System.out.println(exchange.getOut().getHeaders());
System.out.println(exchange.getOut().getBody());
System.out.println("================================");
Exception ex = exchange.getException();
System.out.println(ex.getMessage());
}
#Override
public void onFailure(Exchange exchange) {
System.out.println("Failure");
}
});
Now my webservice is failing with an exception and it is returning a json response:
Response-Code: 500
Content-Type: application/json
Headers: {Content-Type=[application/json], Date=[Fri, 03 Aug 2018 13:18:19 GMT]}
Payload: {"errorMessage":"Unable to process","errorCode":"500"}
Now how can I capture this information in my template.asyncCallback method.
In my above code it is going to onComplete callback method and printing below messages :
Success
Exchange[ID-xxxx-0-2]
Message[ID-xxxx-0-1]
{breadcrumbId=ID-xxxx-0-1, content-type=application/json}
{
"source":"PDF"
}
================================
Message[]
{}
null
================================
I am trying to get the status 500 code and the response payload that has the error information, but I am not able to print anything. Can you please help me what is the correct way to get the error information.

Related

For 400 bad request in cxfrs:rsclient in camel, the exchange is null. For 200 http status, the exchange object is set

Below is the code snippet to consume an api endpoint. For 200 http response, the exchange object contains the payload received. But for 400 response, the payload received is not set in exchange object. Is anything missing in the code below?
Exchange exchange = serviceProducer.send(endPoint, new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.setPattern(ExchangePattern.InOut);
Message inMessage = exchange.getIn();
inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, Boolean.TRUE);
inMessage.setHeader(Exchange.CONTENT_TYPE, "application/json");
inMessage.setHeader(Exchange.HTTP_METHOD, "POST");
inMessage.setHeader(Exchange.HTTP_QUERY, "clientId=" + ClientId);
inMessage.setBody(request);
inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_RESPONSE_CLASS, SearchResponse.class);
inMessage.setHeader(Exchange.CONTENT_TYPE, "application/json");
}
});
SearchResponse searchResponse = (SearchResponse) exchange.getOut().getBody();
getOut() creates a blank output message. You need to use getIn() or getMessage().
SearchResponse searchResponse = (SearchResponse) exchange.getIn().getBody();
https://camel.apache.org/manual/latest/faq/using-getin-or-getout-methods-on-exchange.html#UsinggetInorgetOutmethodsonExchange-UsinggetInorgetOutmethodsonExchange

Axios HTTP DELETE request returns 415 error

I'm building a React.js app with ASP.NET Core as backend following a tutorial course. I'm using Axios to handle HTTP requests, I tested the get, post, put requests working fine, but keep getting error during a del request. Here is the simplified function that produces the same error:
const handleDeleteActivityTest = () => {
Axios.delete('https://localhost:5001/api/activities/e2beb0eb-eaaa-49ee-92d8-daf472210456');
}
And the request prints the following error messages in the console:
DELETE https://localhost:5001/api/activities/e2beb0eb-eaaa-49ee-92d8-daf472210456 415
(Unsupported Media Type)
createError.js:16 Uncaught (in promise) Error: Request failed with status code 415
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:61)
But I tested in Postman, and the delete request works just fine and returns a 200 OK response, and I can see in the database that the entry is deleted:
Postman DELETE response
I would really appreciate if someone can educate me what's going on here. Thank you and stay safe & happy.
EDIT ----------------------------------
Here is the backend code that handles the HTTP request. It is divided into two parts-- 1.) controller that receives the HTTP request and 2.) MediatR class that handles it:
1.)
namespace API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ActivitiesController : ControllerBase
{
private readonly IMediator _mediator;
public ActivitiesController(IMediator mediator)
{
_mediator = mediator;
}
[HttpDelete("{id}")]
public async Task<ActionResult<Unit>> Delete(Guid id, Delete.Command command)
{
command.Id = id;
return await _mediator.Send(command);
}
}
}
2.)
namespace Application.Activities
{
public class Delete
{
public class Command : IRequest
{
public Guid Id { get; set; }
}
public class Handler : IRequestHandler<Command>
{
private readonly DataContext _context;
public Handler(DataContext context)
{
_context = context;
}
public async Task<Unit> Handle(Command request, CancellationToken cancellationToken)
{
var activity = await _context.Activities.FindAsync(request.Id);
if (activity == null)
throw new Exception("Could not find activity");
_context.Remove(activity);
var success = await _context.SaveChangesAsync() > 0;
if (success) return Unit.Value;
throw new Exception("Problem saving changes");
}
}
}
}
I found that the HttpDelete method in the controller receives id as a Guid, but in the React app id is typed as string, so I changed the type to string in the controller and MediatR class, but same http error 415.
Axios doesn't send a content-type on a delete call. It looks like ASP.net core wants a content-type.
As a workaround you could just add a body to the delete call then Axios will add a content-type and that should do the trick.
const handleDeleteActivityTest = () => {
Axios.delete('https://localhost:5001/api/activities/e2beb0eb-eaaa-49ee-92d8-daf472210456', {
data: {foo: 'bar'}
});
}

camel mock - MockEndpoint.whenAnyExchangeReceived process method not execute

I have example code below, why is the process method in MockEndpoint.whenAnyExchangeReceived NOT executed?
I expect the response is "Expected Body from mock remote http call", but the actual response is what passed in request("Camel rocks").
public class CamelMockRemoteHttpCallTest extends CamelTestSupport {
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.to("http://abc/bcd")
;
}
};
}
#Override
public String isMockEndpointsAndSkip() {
return "http://abc/bcd";
}
#Test
public void testSimulateErrorUsingMock() throws Exception {
MockEndpoint http = getMockEndpoint("mock:http://abc/bcd");
http.whenAnyExchangeReceived(new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.getOut().setBody("Expected Body from mock remote http call"); //why this line doesn't execute
}
});
String response = template.requestBody("direct:start", "Camel rocks", String.class);
assertEquals("Expected Body from mock remote http call", response); //failed, the actual response is "Camel rocks"
}
}
I have added some breakpoints to your test and it seems, that automatically created mock endpoint is mock://http:abc/bcd, not mock:http://abc/bcd.
To find, why is this happening, you can look to method org.apache.camel.impl.InterceptSendToMockEndpointStrategy#registerEndpoint, which is called as part of mock endpoint auto registration. There is // removed from http URI. And then to org.apache.camel.util.URISupport#normalizeUri method, where is // added for mock uri prefix.
There is also nice comment in implementation of InterceptSendToMockEndpointStrategy, but I couldn't find it mentioned in documentation.
// create mock endpoint which we will use as interceptor
// replace :// from scheme to make it easy to lookup the mock endpoint without having double :// in uri
When you change it to getMockEndpoint("mock://http:abc/bcd"), the test passes.
The best way to avoid these issues, is pass false as second parameter of getMockEndpoint() method, if you expect already created endpoint. This will throw exception, if mock endpoint does not exists. Otherwise is new mock endpoint created on demand.

Spring websocket MissingSessionUserException: No "user" header in message

I have problem when I try to send message from client to server on Spring websocket.
I have configuration Websocket on server and create #Message on controller.
I send data from client via javascript.
It just work sometimes, but sometimes it fail and throw message on server: MissingSessionUserException: No "user" header in message
Here're my WebsocketConfig:
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/connectsocket").withSockJS();
}
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/");
}
}
Here're my MessageController:
#RestController
public class MessageController {
#Autowired
private SimpMessagingTemplate template;
#MessageMapping("/websocket/message")
public synchronized void message(Message<Object> messageObj,
WebMessage message, Principal principal) throws Exception {
if (principal != null) {
String name = principal.getName();
template.convertAndSendToUser(name, "/topic/dynamic", new MessagePojo("stage", "value", "message"));
}
}
}
Here're my Javascript-backbonejs code:
app.Models.WebsocketModel = Backbone.Model.extend({
fetchData : function() {
console.log("WebsocketModel: fetchData");
var socket = new SockJS(url + "/connectsocket");
var client = Stomp.over(socket);
var onConnect = function(frame) {
console.log('Connected: ' + frame);
client.subscribe("/user/topic/dynamic", function(data) {
var jsonBody = JSON.parse(data.body);
console.log(jsonBody);
});
};
client.connect({}, onConnect);
setInterval(function() {
client.send("/websocket/message", {}, JSON.stringify({
"message" : "Hello world!!!",
"toUser" : "Someone"
}));
}, 10000);
}
});
Here're my server error log:
[2016 Apr 14 - 02:13:19] ERROR:
[org.springframework.web.socket.messaging.WebSocketAnnotationMethodMessageHandler]
- Unhandled exception org.springframework.messaging.simp.annotation.support.MissingSessionUserException:
No "user" header in message at
org.springframework.messaging.simp.annotation.support.PrincipalMethodArgumentResolver.resolveArgument(PrincipalMethodArgumentResolver.java:42)
at
org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
at
org.springframework.messaging.handler.invocation.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:139)
at
org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:108)
at
org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMatch(AbstractMethodMessageHandler.java:490)
at
org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler.handleMatch(SimpAnnotationMethodMessageHandler.java:497)
at
org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler.handleMatch(SimpAnnotationMethodMessageHandler.java:87)
at
org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessageInternal(AbstractMethodMessageHandler.java:451)
at
org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessage(AbstractMethodMessageHandler.java:389)
at
org.springframework.messaging.support.ExecutorSubscribableChannel$SendTask.run(ExecutorSubscribableChannel.java:135)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
You are trying to subscribe to a user destination so the user must be authenticated.
If that is an anonymous user who want to subscribe to the topic, answer to this question will help.
You'll have to assign an anonymous identify to the user and there are two options:
Configure a sub-class of DefaultHandshakeHandler that overrides determineUser and assigns some kind of identity to every WebSocketSession.
The WebSocket session will fall back on the value returned from HttpServletRequest.getUserPrincipal on the handshake HTTP request. You could have a servlet Filter wrap the HttpServletRequest and decide what to return from that method. Or if you're using Spring Security which has the AnonymousAuthenticationFilter, override its createAuthentication method.

throw an exception from org.apache.cxf.jaxrs.ext.RequestHandler handleRequest method

I have this problem using cxf dispatching behavior.
I have developed an Interceptor that implements the org.apache.cxf.jaxrs.ext.RequestHandler interface.
In its "public Response handleRequest(Message m, ClassResourceInfo resourceClass)" method I throw an exception (e.g. a WebServiceException) or a Fault. I have not apparent problems but, on the client side, the client receives a different exception (a ServerWebApplicationException) with the error message empty.
Here the code:
Server side:
public class RestInterceptor implements RequestHandler {
......
#Override
public Response handleRequest(Message m, ClassResourceInfo resourceClass){
.....
throw new WebServiceException("Failure in the dispatching ws invocation!");
.....
}
}
ServerWebApplicationException received on client side:
Status : 500
Headers :
Content-Length : 0
Connection : close
Server : Jetty(7.x.y-SNAPSHOT)
cause=null
detailMessage=null
errorMessage=""
.....
I received the same exception also if I throw a Fault.
What is the problem? I have to use another exception? Why?
Thanks a lot,
Andrea
OK, I've found the solution.I've registered an ExceptionMapper on the dispatcher and use it to encapsulate the exception inside the Response sent to the client.
To do this the interceptor is registered as provider at the web service publication and it implements the "ExceptionMapper" interface. In its "toResponse" method it encapsulates the exception in the Response.
Look at code:
public static<T extends Throwable> Response convertFaultToResponse(T ex, Message inMessage) {
ExceptionMapper<T> mapper = ProviderFactory.getInstance(inMessage).createExceptionMapper(ex.getClass(), inMessage);
if (mapper != null) {
try {
return mapper.toResponse(ex);
} catch (Exception mapperEx) {
mapperEx.printStackTrace();
return Response.serverError().build();
}
}
return null;
}
#Override
public Response toResponse(Exception arg0) {
ResponseBuilder builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR).type("application/xml");
String message = arg0.toString();
return builder.entity(message).build();
}
Andrea
Annotate your exception with #WebFault
example :
#WebFault(name = "oauthFault")
public class OAuthException extends RuntimeException {
...
}

Resources