I have the following route defined using Spring DSL:
<camelContext id="myapp-camel-ctx" errorHandlerRef="deadLetterErrorHandler"
xmlns="http://camel.apache.org/schema/spring">
<route id="myapp-camel-route">
<from uri="timer://runOnce?repeatCount=1&delay=10" />
<to uri="bean:fizzBean?method=doFizz" />
<!-- What I call the "Smooks processor" -->
<to uri="smooks://my-smooks-config.xml" />
<to uri="bean:buzzBean?method=doBuzz" />
</route>
</camelContext>
<bean id="deadLetterErrorHandler" class="org.apache.camel.builder.DeadLetterChannelBuilder">
<property name="deadLetterUri" value="bean:errorCatcher" />
</bean>
<bean id="errorCatcher" class="com.me.myorg.myapp.ErrorCatcher">
<property name="foo" value="BAR" />
</bean>
Sometimes, depending on the output (outbound message) of the fizzBean, the Smooks processor throws an exception and hangs the entire application. When it does this, I can see the exception being thrown in the app logs (it's actually a MySQL exception), but not sure how to wrap/catch it and continue processing. I thought that, given the ErrorCatcher setup above, the thrown MySQL exception would be handled and that the route would continue processing. Instead, I never see evidence in my app logs that the ErrorCatcher#handle method gets executed when these Smooks/MySQL exceptions are thrown.
Have I configured anything incorrectly here? Is there anything more I can do (either via the Smooks processor's URI configs or something else) to prevent exceptions being thrown from inside that processor from hanging the entire app? Thanks in advance!
It depends how the Smooks team have implemented their Camel component. The Camel error handler only kicks in, if an exception was thrown, which Camel can catch; or there was an exception explicit set on the Exchange using setException. If Smooks do not do that (maybe they catch the exception, and dont propagate that back to Camel), then Camel cannot detect that exception and react upon it.
If you want to see what goes on at runtime, you can enable the tracer
http://camel.apache.org/tracer
Also mind that when you use a bean to handle the exception with the error handler. Then read this FAQ how to get access to the caused exception: http://camel.apache.org/why-is-the-exception-null-when-i-use-onexception.html
your configuration seems to be correct, if you want to see that it works, you can change your handle methods signature as follows
public void handle(Exception exception, Exchange exchange) {
System.out.println("Got Exception..."+exception.getMessage());
System.out.println("Exchange is :"+exchange);
}
now you can see the result on the console...
Related
What i'm trying to do is create a proof-of-concept Camel route, that exposes a cxfrs service endpoint. Requests to this endpoint are routed to another service on a different server using cxf client. After i get the xml response, i need to do some stuff with it, lets say save the response body to a DB for example. And of course the original requestor needs to receive the response as well.
If i don't do any post-processing of the response, then i get the response xml in the browser as expected. But any time i try to add another step to my route for processing the response, the browser gets a response that is empty. As you can see in the commented out lines, it doesn't matter which camel component i use the call the bean. I tried bean, process, and to. Even if i comment out all the code from the bean so it does nothing, the result is the same.
Here's my route:
<cxf:rsServer address="{{base.url}}/employeeservicecxf" id="restServiceCxf">
<cxf:serviceBeans>
<bean class="com.kf.camel.sample.EmployeeServiceResource"/>
</cxf:serviceBeans>
</cxf:rsServer>
<cxf:rsClient
address="http://{{remote.server}}/adminrest/jaxrs/projects/10475/products"
id="rsClient" loggingFeatureEnabled="true" />
<bean class="com.kf.camel.sample.CamelProcessor" id="processor"/>
<bean class="com.kf.camel.sample.CamelResponseProcessor" id="responseProcessor"/>
<camelContext id="_camelContext1" trace="true" xmlns="http://camel.apache.org/schema/spring">
<propertyPlaceholder id="properties" location="app.properties"/>
<route id="_route1">
<from id="_from1" uri="cxfrs://bean://restServiceCxf"/>
<process id="_process1" ref="processor"/>
<setHeader headerName="CamelHttpMethod" id="_setHeader1">
<constant>GET</constant>
</setHeader>
<to id="_to1" uri="cxfrs://bean://rsClient"/>
<!-- to id="_to3" uri="bean://com.kf.camel.sample.CamelResponseProcessor?method=process"/-->
<bean id="_bean1" ref="responseProcessor" method="process"/>
<!-- process id="_process2" ref="responseProcessor"/-->
</route>
</camelContext>
</beans>
Response Headers
Response Body with content length mismatch error
Have you tried to enable stream caching?
Sounds like any first operation you do on the response is consuming the stream and any further attempt to read the stream again gets an empty result.
When you want to read a stream multiple times in Camel you have to enable stream caching.
I'm facing a weird behavior when a conversion error occurs using a message converter in my inbound gateway. The idea in the example below is to receive XML payloads (or serialized java), convert them to java objects and respond with the same media type.
Given this configuration:
<bean id="converterXml" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" />
<bean id="converterSerialized" class="org.springframework.integration.http.converter.SerializingHttpMessageConverter" />
<util:list id="converters">
<ref bean="converterXml" />
<ref bean="converterSerialized" />
</util:list>
<int-http:inbound-gateway id="inboundIntegrationGateway"
view-name="/integration"
message-converters="converters" error-channel="errorChannel"
request-payload-type="MyXmlPojo"
supported-methods="POST" request-channel="inboundIntegration"
path="/services/integration"
reply-timeout="50000">
</int-http:inbound-gateway>
If an invalid XML payload (no end tag for example) is submitted, the exception HttpMessageNotReadableException raised in the JAXB XML converter is not forwarded in the errorChannel (where I defined a service activator to handle exceptions). Note that this handler works well after the payload conversion.
<int:service-activator input-channel="errorChannel" ref="exceptionHandlerService"
method="handleException" requires-reply="true" />
What am I missing here? Why is the HttpMessageNotReadableException not handled by my error handler? Any help is welcome!
Why is the HttpMessageNotReadableException not handled by my error handler?
The error-channel comes into force only when we already send Message to the downstream flow, but if your incoming HTTP request can't be converted to Message, there is still nothing to send to the error-channel.
Right, with convertExceptions = true we can't just return an exception as is and let some HttpMessageConverter to convert it into a reasonable HTTP response. Typically SerializingHttpMessageConverter is on the scene.
Why is this exception not wrapped in a MessageHandlingException and sent to my error handler?
Just because we haven't reached messaging yet. With your problem we are still in the standard Spring MVC environment and Spring Integration is still powerless here during request conversion.
You should consider some solution from Spring MVC for your case: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers
Adding convert-exceptions="true" to your int-http:inbound-gatewaycould solve the exception flow problem. This post has been useful for me.
I have a simple route defined in a routeContext in Camel (this route will be used in multiple routes).
<route id="sendToRabbitQueue">
<from uri="direct:sendToQueue" />
<convertBodyTo type="java.lang.String"/>
<setHeader headerName="rabbitmq.ROUTING_KEY">
<constant>my.routing.key</constant>
</setHeader>
<to uri="ref:genericRabbitEndpoint"/>
</route>
And I have an endpoint (defined in an endpoints file)
<endpoint id="genericRabbitEndpoint" uri="rabbitmq://${rabbitmq.host}:${rabbitmq.port}/${rabbitmq.exchange.name}">
<camel:property key="autoDelete" value="false" />
<camel:property key="connectionFactory" value="#rabbitConnectionFactory" />
</endpoint>
Yes - I have seen the http://camel.apache.org/rabbitmq.html page - that's where I got the idea to set the header on the exchange. However no message is being published on the queue. I'm clearly overlooking something and any help would be appreciated.
So this seems like a bit of a gotcha and the answer relates to part of the route I didn't include in the question because I didn't think it was relevant.
The route starts at a RabbitMQ endpoint (not included above). As a result the exchange has some RabbitMQ headers set when it arrives:
rabbitmq.ROUTING_KEY
rabbitmq.EXCHANGE_NAME
rabbitmq.DELIVERY_TAG
These headers are used across the life of the route and appear to override the values when I try to publish at a different RabbitMQ endpoint. The way I've fixed is by introducing a bean which strips the headers out. Not ideal behaviour in my opinion...
public void stripRabbitHeaders(#Headers Map headers)
{
headers.remove("rabbitmq.ROUTING_KEY");
headers.remove("rabbitmq.DELIVERY_TAG");
headers.remove("rabbitmq.EXCHANGE_NAME");
}
I've read that if exception is thrown in the flow the first thing the framework will do is check message header for error-channel property. Is it always the case?
In my particular case I'm assigning a custom error-channel to a message header and yet the message seems to get propagated up the stream to the nearest error-handler/error-channel.
<int:chain id="buildAggregatedResponseChain" input-channel="aggregatedResultChannel"
output-channel="sendAggregatedChannel">
<int:header-enricher>
<int:error-channel ref="myErrorChannel"/>
</int:header-enricher>
<int:service-activator ref="service" method="doSomething"/>
</int:chain>
I explicitly throw an exception inside doSomething but the exception never ends up in myErrorChannel. Instead, it is "propagated" to the nearest ErrorHandler up the stream or to the error-channel specified up the stream for int-mail:imap-idle-channel-adapter(tried several different flows).
What do I miss? Maybe someone can outline the main principal of error handling/error propagation(for example when talking about several transactions, etc)? There is some information out there, but it is quite scattered and not systematic.
It depends on the upstream flow; if there's an async handoff the header is consulted; otherwise, the exception is thrown back to the inbound endpoint.
In general, I would advise against modifying framework headers such as errorChannel. Instead put an error-channel on the inbound endpoint (such as your imap idle adapter) and handle the errors on that flow.
Modifying the headers directly is rarely needed. If you wish to insert different error-handling mid-flow then you can insert a messaging gateway...
<int:service activator ... ref="gw" />
<int:gateway id="gw" default-request-channel="..."
error-channel="midFlowErrorChannel" />
If the downstream flow (from the gateway) returns no result on success, then be sure to add a default reply timeout of 0 (or use a custom service interface with a method that returns void).
I am using spring-jms with active mq.
I have a ErrorHandler listener set up, that receives a callback each time a jms message delivery fails - i.e. the method that processes the message throws an exception instead of returning gracefully.
However my jms is configured to retry several times until the jms delivery finally succeeds. And my callback is notified of all of the failures.
What I want is a listener that receives a notification only when all the retries finally fail. The motivation is to bring the issue up for the admin's attention. But I don't want spurious notifications in the admin's console.
<bean abstract="true" id="abstractDestinationListener"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsFactory" />
<property name="errorHandler" ref="destinationErrorHandler"/>
<property name="sessionTransacted" value="true"/>
</bean>
What you might consider is using a per-destination dead-letter queue (individualDeadLetterStrategy - https://activemq.apache.org/message-redelivery-and-dlq-handling.html) for the destinations in question. When the maximum redelivery count has been hit, the message is moved out to the DLQ. You can then set up a consumer on that queue with a listener that emails the administrator.
Another way to do it might be to wrap your listener with a try-catch, and rethrow any exceptions only if message.getIntProperty("JMSXDeliveryCount") < MAX_REDELIVERY_COUNT, and email the admin otherwise. However, that option means placing your redelivery limit in two places - the broker config and the code.
if you need more fine-grained control/flexibility/routing with JMS messaging/handling, then you should just use Apache Camel's error handling...