I'm building an exception handler for a jboss/camel project. Catching the exception with an onException clause works, and I can access the caught exception from a bean, but that work's only directly in exception handler. Since I need this handler in multiple different projects, I want to direct from the handler to a different route (in a different context), and handle the exception there centrally. But after the routing, the caught exception is not accessible any longer. Why is this, and how can I fix that. I'd like not to have to add another EJB to all of my projects.
Code:
<camelContext id="project1Context" xmlns="http://camel.apache.org/schema/spring">
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<bean ref="projectSpecificBean" method="peekException" />
<to uri="activemq:queue:centralExceptionHandling" />
</onException>
[... the routes of the context, where the exception happens ...]
</camelContext>
In the bean:
public void peekException(Exchange camelExchange) {
Exception e = camelExchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
log.warn("Caught general Exception", e);
}
This gives me access to the Exception inside the bean. But when I remove the <bean ref="projectSpecificBean" method="peekException" /> from the onException clause and add the same thing to the route at activemq:queue:centralExceptionHandling (to a bean local to that project there, in a different camelContext), I cannot find the exception anywhere in the exchange.
To be able to receive exception on the client of ActiveMQ queue you need use camel jms component option "transferExchange". This option must be enabled on both sides (during sending to ActiveMQ queue and receiving from it). In that case not only message body/headers will be sent but whole exchange will be transfered. See http://camel.apache.org/jms.html (transferExchange option). In general it will then looks like there are no queue between exception route and exception handler route. Here are some details from documentation:
You can transfer the exchange over the wire instead of just the body and headers. The following fields are transferred: In body, Out body, Fault body, In headers, Out headers, Fault headers, exchange properties, exchange exception. This requires that the objects are serializable. Camel will exclude any non-serializable objects and log it at WARN level. You must enable this option on both the producer and consumer side, so Camel knows the payloads is an Exchange and not a regular payload.
Example of code:
<camelContext id="project1Context" xmlns="http://camel.apache.org/schema/spring">
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<bean ref="projectSpecificBean" method="peekException" />
<to uri="activemq:queue:centralExceptionHandling?transferExchange=true" />
</onException>
[... the routes of the context, where the exception happens ...]
</camelContext>
// Somewhere in the central exception handler.
<camelContext id="exceptionHandlerContext" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="activemq:queue:centralExceptionHandling?transferExchange=true" />
<!-- Here you will have almost completely the same exchange as it was before it was sent to ActiveMq -->
</route>
</camelContext>
Related
I have two Apache Camel Routers in a Docker Network.
The one acts as client and sends some data in the body to the outer with a route.
I now want to get the modified body from the server router.
But apparently the body modification is never applied.
(For context the initial request is a post request where the final body should be the response)
Here is how my "client" route looks like:
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<restConfiguration component="restlet" bindingMode="json" port="8989" enableCORS="true"/>
<rest path="/finData">
<description>User rest service</description>
<post>
<to uri="direct:update"/>
</post>
</rest>
<route id="sendFinData">
<from uri="direct:update"/>
<log message="Got some data: ${body}"/>
<to uri="aclient://otherClient:9191"/>
</route>
And here is how the "server" looks like:
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route id="receiveFinData">
<from
uri="aserver://0.0.0.0:9191"/>
<log message="Received via data: ${body}"/>
<setBody>
<simple>{"result": true }</simple>
</setBody>
</route>
Update: If I add a second route to the "client" and call this one instead the external of the "server" and modify the body there it is working
Update due to comments: When the setBody is not the problem, then I have to ask about the flow in the code.
Are you calling /finData to send a request from the client to the server?
But who is listening on aclient://otherClient:9191? This route is not in your question.
And the caller of aserver://0.0.0.0:9191 is also not in your question.
Can you post the whole flow?
Original answer
I think #tadayoshi-sato already commented the solution to your problem.
You use the Camel Simple language (an Expression Language) to set a constant string into your message body.
Use the Camel Constant language instead.
<setBody>
<constant>{"result": true }</constant>
</setBody>
I am very new to camel. I want to pull some xml file from a sftp location and feed the content to existing REST endpoint. Below is my configuration,
<route id="xml.FOEBRE">
<from uri="<sftp server uri>"/>
<marshal>
<string/>
</marshal>
<setHeader headerName="Content-Type">
<constant>application/xml</constant>
</setHeader>
<doTry>
<to uri="<REST URI>?httpMethod=POST"/>
<bean ref="myListener" method="onPushSuccess"/>
<doCatch>
<exception>java.lang.Exception</exception>
<!--<log message="error : ${exception.message}" loggingLevel="ERROR"/>-->
<process ref="globalExceptionListener"/>
</doCatch>
</doTry>
</route>
I am able to call it, but I want to capture the response status and body of the REST call. In my success processor I have written,
Message message = exchange.getIn();
int status = message.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
LOG.debug("Response status : {}", status);
LOG.debug("Payload successfully sent to securecargo");
String body = message.getBody(String.class);
LOG.debug("Response Body : {}", body);
This code is returning me the proper response body. But the same code (in catch listener) is not working when the status code is 400. I have two question,
How to catch this response body when status code is 400? My processor in catch block is never invoked.
Why I getting this body in exchange.getIn(), not in exchange.getOut()?
Note: My REST service returns two status, 201 for success and 400 for failure. I know my service returns a JSON payload both in success and failre scenario.
You'll get the response code from headers inside getIn() method instead. By facing any issue (I mean, HTTP-call errors) other than the expected response, an exception will be thrown and set into the exchange properties (Exchange.EXCEPTION_CAUGHT).
Finally I got the answer
My processor was getting invoked, but encounters a NPE while excuting the processor body and that exception was internally consumed by camel
During an exception, all exception related information can be retrieved from exception object itself rather than In/Out
Below is my exception handler (processor) code,
HttpOperationFailedException cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, HttpOperationFailedException.class);
LOG.error(cause.getMessage());
final String responseBody = cause.getResponseBody();
LOG.error(responseBody);
I have this camel route:
from("cxf:bean:endpointDocs01?loggingFeatureEnabled=true")
.to("direct:CBR")
.transform().method(WebServiceUtils.class,"response()")
.log("Outbound message: ${body}");
endpointDocs01 is defined in the blueprint like this:
<cxf:cxfEndpoint address="/documentos/" id="endpointDocs01"
serviceClass="com.adelco.webservice.ServiceDocs" wsdlURL="wsdl/wsdl03.wsdl">
<cxf:properties>
<entry key="schema-validation-enabled" value="true"/>
</cxf:properties>
</cxf:cxfEndpoint>
This route works with no problems including the schema-validation.
When I send a correct request, I can do "things" (in this case logging) using the last line of the exchange ".log("Outbound message: ${body}". In this case, the log shows this:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<outputDocumento xmlns="http://webservice.adelco.com">
<respuesta>0</respuesta>
<mensaje>El mensaje [113282] fue recibido. Fecha recepciĆ³n Wed Apr 12 17:01:11 CLT 2017</mensaje>
</outputDocumento>
</soap:Body>
</soap:Envelope>
But, when I send a incorrect request, the line ".log("Log outbound message: ${body}" does nothing. However I get a response in the client (a Soap:Fault response)
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>Unmarshalling Error: cvc-complex-type.2.4.a: Invalid content was found starting with element 'Sociedad'. One of '{"http://webservice.adelco.com":TipoMovimiento}' is expected.</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
Why this soap:Fault response in not logged?
Your route is invoked after unmarshalling. So if unmarshalling fails because of invalid input, the route does not trigger and does not log.
Read this article about CXF architecture.
Phase Interceptors
CXF provides an InterceptorChain implementation called the PhaseInterceptorChain. [...]
Let us take a hypothetical simplified example (NOTE: these phases and interceptors don't necessarily exist in CXF). Let us say we are parsing a SOAP message. We may want to have two phases. First, a dispatch phase which parses the soap headers and determines which service to route the Message to. Second, an unmarshal phase which binds the SOAP body to JAXB objects.
Fault Handling
At any point during processing, an interceptor may throw a Fault, or a derivative of a Fault like the SoapFault. This will cause the chain to stop invoking and unwind it. Unwinding consists of calling handleFault on each interceptor that was invoked in reverse order.
When a Fault occurs the processing is stopped and the interceptor chain unwinded. CXF uses different chains for messages (in & out) and Faults (in & out).
To use custom beans (which must implement PhaseInterceptor interface) as interceptors:
<cxf:cxfEndpoint address="/documentos/" id="endpointDocs01"
serviceClass="com.adelco.webservice.ServiceDocs" wsdlURL="wsdl/wsdl03.wsdl">
<cxf:properties>
<entry key="schema-validation-enabled" value="true"/>
</cxf:properties>
<cxf:inInterceptors>
<ref component-id="inInterceptorBean" />
</cxf:inInterceptors>
<cxf:outInterceptors>
<ref component-id="outInterceptorBean" />
</cxf:outInterceptors>
<cxf:outFaultInterceptors>
<ref component-id="outFaultInterceptorBean" />
</cxf:outFaultInterceptors>
</cxf:cxfEndpoint>
As #Allesandro has said, the chain would unwind on failure.
You could've added an onException clause:
onException(ValidationException.class)
.log("Outbound message: ${body}");
from("cxf:bean:endpointDocs01?loggingFeatureEnabled=true")
.to("direct:CBR")
.transform().method(WebServiceUtils.class,"response()")
.log("Outbound message: ${body}");
I'm face with a situation where we cannot use schema to validate incoming request (basically schema is there but it accepts any String in request, wsdl designers have their own reasons to do that to accept request from different sources and flexibility). But when the request is received, I validate that the child element of request wrapper is what we expect (using XPath for that). Now if the child element is not what expected, I'd like to throw Soap Fault with Client code and may be include error message that schema validation failed, request doesn't contain valid element.
I'm using Mule 3.3 and doing my XPath validation in <choice> element and I want to throw exception in <otherwise> block.
Is there a way to throw Soap Fault manually in mule flow and
How to add custom fault string. I'm not sure if an outInterceptor will solve the purpose as I'm not using schemaValidation attribute of <cxf:proxyService>.
Here is part of my flow
<http:inbound-endpoint address="${service.address}" exchange-pattern="request-response">
<cxf:proxy-service wsdlLocation="classpath:service.wsdl" namespace="http://company.com/services/service" service="CompanyService" />
</http:inbound-endpoint>
<choice>
<when>.....</when>
<otherwise><!-- Here I want to throw Soap Fault ---></otherwise>
</choice>
<catch-exception-strategy>
<flow-ref name="generateErrorResponse" />
</catch-exception-strategy>
Since you are using a cxf:proxy-service you have complete control on the response. For example, if you add the following in your otherwise block, you'll be able to create whatever SOAP fault you want:
<expression-component><![CDATA[
message.payload = '<soap:Fault xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'
+ '<faultcode>A code</faultcode><faultstring>A string</faultstring>'
+ '</soap:Fault>';
]]></expression-component>
i have a flow that works as follows:
<flow name="ChatListener">
<quartz:inbound-endpoint jobName="eventTimer"
repeatInterval="${chatListener.pollingInterval}">
<quartz:event-generator-job />
</quartz:inbound-endpoint>
<filter ref="ActiveTrainingFilter" />
<component>
<singleton-object class="com.ChatListener.ChatListener" />
</component>
<not-filter>
<payload-type-filter expectedType="org.mule.transport.NullPayload" />
</not-filter>
<collection-splitter />
<vm:outbound-endpoint path="ChatMsgs"
exchange-pattern="one-way" />
<default-exception-strategy>
<vm:outbound-endpoint path="ErrorMsgs"/>
</default-exception-strategy>
</flow>
Now, what is actually hapenning is that the generator calls the singleton compnent which does some DB retrieval and returns a collection of a java bean class which i later split (the null filter is in case i want to stop the flow).
My problem is - suppose i have an error connecting to the db and the component's oninitialise fails. What would keep the generator from trying to call the component over and over again? (and producing an error every time).
It looks to me as if i've handling something not quite right, So what would be the good to implement it to deal with exceptions?
Any ideas? thanks in advance!
I would try the following:
Declare the quartz endpoint in a global manner,
Use it in the flow.
Create a custom exception strategy and use it in the flow too.
Inject the global quartz endpoint in this custom exception strategy.
In this strategy, if the exception caught is one you deem characteristic to unrecoverable failure, call endpoint.stop().
You would need to go through JMX to restart the endpoint after clearing the error condition.