I'm testing exception handling in Camel, and noticed an odd behaviour. I have an http component with deliberately throws a 404 Error. Camel would then throw a HttpOperationFailedException created by org.apache.camel.component.http.HttpProducer.populateHttpOperationFailedException I'm using onWhen to distinguish between different status codes:
This is a sample of the approach for a 404:
onException(HttpOperationFailedException.class)
.onWhen(new Predicate() {
public boolean matches(Exchange exchange) {
HttpOperationFailedException ex = exchange.getProperty(
Exchange.EXCEPTION_CAUGHT,
HttpOperationFailedException.class);
logger.warn("*** 404 Error ***");
return (ex.getStatusCode() == 404);
}
}).handled(true).maximumRedeliveries(0);
But it never gets executed.
So then I tried this:
onException(Exception.class)
.onWhen(new Predicate() {
public boolean matches(Exchange exchange) {
Object ex = exchange.getProperty(Exchange.EXCEPTION_CAUGHT);
logger.error(ex.getClass());
return (true);
}
}).handled(true).maximumRedeliveries(0);
And the logger always shows class org.apache.camel.http.common.HttpOperationFailedException
Any help would be great! I'm using Camel 2.17.1.
Related
I made an instance of Camel Context and added the routes in the context as follows:
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
System.out.println("in add route method");
from("direct:start").routeId("contextRoute")
.setHeader("client_id", constant("abc"))
.setHeader("client_secret", constant("clk"))
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.setHeader(Exchange.HTTP_URI, constant(url))
.marshal(gsonDataFormat)
.log("trying to send message")
.to(url)
.log("response is ${body}");
}
});
camelContext.start();
However I am not able to get a log of my messages and debug the route. Is there an issue with the route or is there an issue with the log files or anything?
I am new to Camel and learning to setup routes.
So I started with a simple scenario, a URL that I hit and it returns me some data. For this example I have used http://services.groupkt.com/country/get/all for returning that data.
This is the setup for my path
from("direct:greet")
.autoStartup(true)
.routeId("greet")
.setHeader(Exchange.HTTP_METHOD, constant(HttpMethods.GET))
.to("http4://services.groupkt.com/country/get/all")
Now I have requestMapping to a URL /check and when I hit this URL http://localhost:8080/check it returns this
{
"timestamp": 1527882311362,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/check"
}
I was expecting the JSON response to show up with the data for all the countries listed which you see when you hit the URL(http://services.groupkt.com/country/get/all) in your browser.
The mapping is in other class:
#RequestMapping(value = "/check", method = RequestMethod.GET)
public String get(#RequestParam(value = "name") String name) {
return serviceProcessor.getServiceResponse(name);
getServiceResponse goes as follows:
public String getServiceResponse(String name) {
final ModelCamelContext context = userServiceRoute.getContext();
final ProducerTemplate template = new DefaultProducerTemplate(context);
try {
template.start();
} catch (Exception e) {
LOGGER.error("Error starting producerTemplate with userServiceRoute" + e);
}
final Endpoint endpoint = context.getEndpoint("direct:greet");
template.setDefaultEndpoint(endpoint);
return template.requestBody((Object)name, String.class);
}
Is there something wrong with the path setup or the approach itself is wrong here?
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.
I try to define a CXF endpoint, but the it doesn't work.
When I want to address the endpoint then I'v got the "bean not found" exception.
CXF definition:
#Override
public void configure() throws Exception
{
errorHandler(deadLetterChannel(systemInfo.getQueuName())
.allowRedeliveryWhileStopping(true)
.maximumRedeliveries(-1)
);
onException(Exception.class).process(routeHandlingBean);
CamelContext camelContext = getContext();
CxfEndpoint partnerTestService = new CxfEndpoint();
partnerTestService.setEndpointNameString("partnerTestService");
partnerTestService.setAddress("http://localhost:9081/MockPartnerService");
partnerTestService.setWsdlURL("http://localhost:9081/MockPartnerService?wsdl");
partnerTestService.setServiceClass(aaa.bbb.ccc.service.PartnerService.class);
partnerTestService.setServiceNameString("partnerTestService");
partnerTestService.setDataFormat(DataFormat.CXF_MESSAGE);
partnerTestService.setCamelContext(camelContext);
try {
camelContext.addEndpoint("partnerTestService", partnerTestService);
} catch (Exception e) {
e.printStackTrace();
}
When I try to call the endpoint:
cxf:bean:partnerTestService
Then I have got this error message:
org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: cxf://bean:partnerTestService due to: No bean could be found in the registry for: partnerTestService of type: org.apache.camel.component.cxf.CxfEndpoint
I don't know what else I have to set up.
Thx!
Feri
The solution is the usage of CxfEndpoint class. I wrote a function what gives back a needed object (instead of string endpoint), and so it is work.
The route definition:
ValueBuilder dynRouterMethod = method(esbDynRouter, "endpointRoute");
from(queueName).id(systemInfo.getRouteId()).routeId(systemInfo.getRouteId()).autoStartup(false)
.transacted()
.log(LoggingLevel.INFO, systemInfo.getRouteId() + " route usage.")
.beanRef("statusUpdaterBean", "updateSendingStatus")
.dynamicRouter(dynRouterMethod)
.beanRef("statusUpdaterBean", "updateSentStatus")
;
The procedure (it called in dynamic router procedure):
private CxfEndpoint getCxfEndpoint(DispConfView target) throws Exception
{
CxfEndpoint cxfEndpoint = (CxfEndpoint) camelContext.getEndpoint(<Something>);
if (cxfEndpoint == null)
{
String serviceClassNme = <class name what represent the POJO class>;
String methodName = <WS methode name>;
cxfEndpoint = new CxfEndpoint();
cxfEndpoint.setAddress(methodName);
cxfEndpoint.setDataFormat(DataFormat.POJO);
cxfEndpoint.setServiceClass(serviceClassNme);
cxfEndpoint.setCamelContext(camelContext);
camelContext.addEndpoint(<Something>, cxfEndpoint);
}
return cxfEndpoint;
}
This solution work if the message is POJO-type.
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 {
...
}