Camel - How I can define a errorHandler and can use includeRoutes? - apache-camel

I define my routes in Subclasses and get them in one RouteBuilder together with includeRoutes(). Now I want to insert a default ErrorHandler but I get the Error Message:
errorHandler must be defined before any routes in the RouteBuilder
This is my Code:
public class defaultRoutes extends RouteBuilder {
public void configure() throws Exception {
errorHandler(deadLetterChannel("direct:deadLetter").maximumRedeliveries(3));
from("direct:deadLetter").id("deadLetter")
.errorHandler(defaultErrorHandler().disableRedelivery())
.log("${exception.stacktrace}")
.setHeader("ErrorMessage",simple("${exception}",String.class))
.setHeader("ErrorStacktrace",simple("${exception.stacktrace}",String.class))
.to("activemqWithoutTransactions:errors");
...
...
for(String Module: globalConfig.getLoadedModules()) {
...
includeRoutes(routes);
}
...
}
I had also test this:
public class defaultRoutes extends RouteBuilder {
public void configure() throws Exception {
RouteBuilder errorHandler = new RouteBuilder() {
public void configure() {
errorHandler(deadLetterChannel("direct:deadLetter").maximumRedeliveries(3));
from("direct:deadLetter").id("deadLetter")
.errorHandler(defaultErrorHandler().disableRedelivery())
.log("${exception.stacktrace}")
.setHeader("ErrorMessage",simple("${exception}",String.class))
.setHeader("ErrorStacktrace",simple("${exception.stacktrace}",String.class))
.to("activemqWithoutTransactions:errors");
}
};
includeRoutes(errorHandler);
...
...
for(String Module: globalConfig.getLoadedModules()) {
...
includeRoutes(routes);
}
...
}
But this throw also the same Error Message! What can I do, to get the ErrorHandler to work?
Thank you

I think your problem is described in the comments of the issue https://issues.apache.org/jira/browse/CAMEL-5612.
If you use a global scoped errorHandler you have to ensure that it should like the
following:
errorHandler
errorHandler
route
route
and not:
errorHandler
route
errorHandler
route
kind regards,
soilworker

Try removing the .errorHandler() line from your route:
from("direct:deadLetter").id("deadLetter")
.errorHandler(defaultErrorHandler().disableRedelivery()) // this is incorrect
.log("${exception.stacktrace}")
.setHeader("ErrorMessage",simple("${exception}",String.class))
.setHeader("ErrorStacktrace",simple("${exception.stacktrace}",String.class))
.to("activemqWithoutTransactions:errors");
Check this page for more usage examples on the Dead Letter Channel in Camel.

Related

Processor adding to Camel RouteBuilder

I have 2 different styles of routes that I would like to understand. I hope someone can help.
Style #1.
#Component
public class Route1 extends RouteBuilder {
...
from(RouteName1)
.process(new Processor1())
...
}
public class Processor1 implements Processor {
...
#Override
public void process(Exchange exchange){
..
FileOperations.moveFile(String src, String dest);
}
}
public class FileOperations{
public static void moveFile(..) {
// issue #1
org.apache.camel.util.FileUtil.renameFile(src, dest, true);
File fileToCheck = new File(dest);
java.nio.file.Files.exists(fileToCheck.toPath())
}
Style #2
#Component
public class Route2 extends RouteBuilder {
...
from(RouteName2)
.process("Processor2")
...
}
#Component("Processor2")
public class Processor2 implements Processor {
...
#Override
public void process(Exchange exchange) {
org.apache.camel.util.FileUtil.renameFile(..);
}
}
The issue I am currently facing is that issue #1 renameFile is not reliable. often renameFile returned true and Files.exists check return true incorrectly while the file is not in the destination directory.
So far, Style #2 didn't face that issue.
The way to config route with processor are different. Is it somehow causing this issue? Or anything I miss here?
dependencies
camel-spring-boot-starter:3.9.0...
camel-jdbc, sql,support:3.9.0
spring-boot-starter-xxx: 2.7.1
Thank you!

Apache Camel - call simple language without side effect

Is it possible to call an object with the Simple language directly within the route and without side effects? The 2 approaches i've tried are;
.toD("${header.exchangeHelper.inc1()}") //works but fails if there is a return type from the called method
.bean(new Simple("${header.exchangeHelper.inc1()}")) //works but sets the body to false
Neither of which give the ideal solution.
You can store the result to exchange property or header instead. This way you'll keep the original body and get the result from your method in case you need it later. Alternatively you can just call the method using a processor.
These are generally better approaches with Java-DSL for something like this than using simple-language since they benefit from IDE's refactoring tools, error highlighting and many forms of linting.
package com.example;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.apache.camel.Exchange;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class ExampleTest extends CamelTestSupport {
ExchangeHelper exchangeHelper = mock(ExchangeHelper.class);
#Test
public void useSetPropertyTest() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).body().isEqualTo("Hello");
when(exchangeHelper.inc1()).thenReturn(true);
template.sendBodyAndHeader("direct:useSetProperty", "Hello",
"exchangeHelper", exchangeHelper);
verify(exchangeHelper, times(1)).inc1();
resultMockEndpoint.assertIsSatisfied();
}
#Test
public void justUseProcessorTest() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).body().isEqualTo("Hello");
when(exchangeHelper.inc1()).thenReturn(true);
template.sendBody("direct:justUseProcessor", "Hello");
verify(exchangeHelper, times(1)).inc1();
resultMockEndpoint.assertIsSatisfied();
}
#Test
public void useHeaderFromProcessorTest() throws Exception {
MockEndpoint resultMockEndpoint = getMockEndpoint("mock:result");
resultMockEndpoint.expectedMessageCount(1);
resultMockEndpoint.message(0).body().isEqualTo("Hello");
when(exchangeHelper.inc1()).thenReturn(true);
template.sendBodyAndHeader("direct:useHeaderFromProcessor", "Hello",
"exchangeHelper", exchangeHelper);
verify(exchangeHelper, times(1)).inc1();
resultMockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder(){
#Override
public void configure() throws Exception {
from("direct:useSetProperty")
.setProperty("result")
.simple("${header.exchangeHelper.inc1()}")
.log("Body: ${body} Result header: ${exchangeProperty.result}")
.to("mock:result")
.removeProperty("result");
from("direct:justUseProcessor")
.process( ex -> { exchangeHelper.inc1(); })
.log("Body: ${body}")
.to("mock:result");
from("direct:useHeaderFromProcessor")
.process( ex -> {
ex.getMessage()
.getHeader("exchangeHelper", ExchangeHelper.class)
.inc1();
})
.log("Body: ${body}")
.to("mock:result");
}
};
}
interface ExchangeHelper {
public boolean inc1();
}
}
Not tried, but why not using the wiretap EIP to issue an extra (and separated!) request to your requestHelper method ?
Something like:
from("direct:demo")
.wireTap("bean:${header.exchangeHelper.inc1()}")
.to("direct:doSomething");
You can simply use Camel Script, something like that:
from("direct:exampleScript")
.script().simple("${header.exchangeHelper.inc1()}")
.log("End")
;

Simple Camel test fails with no messages recieved

Am using Spring Boot and I have just added camel to it.
I have a simple camel route setup :
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
#Component
public class MyRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("file://in").to("file://out");
}
}
When I try to create simple test for this route with :
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class MyRouteTest extends CamelTestSupport {
#Autowired
private CamelContext camelContext;
#Produce(uri = "file://in")
private ProducerTemplate producerTemplate;
#EndpointInject(uri = "mock:file://out")
private MockEndpoint mockEndpoint;
#Test
public void routeTest() throws Exception {
mockEndpoint.expectedMessageCount(1);
producerTemplate.sendBody("Test");
mockEndpoint.assertIsSatisfied();
}
}
It fails with
mock://file://out Received message count. Expected: <1> but was: <0>
Not sure what could be a problem here. I have producer template that has uri as my route from point and am mocking to endpoint with EndpointInject and the the mock uri?
Fixed but not 100%
If I change route from real one
from("file://in").to("file://out");
to
from("file://in").to("mock:out");
And in my test override
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new MyRoute();
}
to create specific route
and strangest of all ! Had to remove :
#SpringBootTest
and after that
private CamelContext camelContext;
And then it started working !
But unfortunately not what I need, still there are things that need to be fixed, I would like to use my real prod route !
from("file://in").to("file://out");
And if possible not use advise on route , but just mock it , tried with
mock:file://out in test, but it didnt work :(
and also , it does not work with #SpringBootTest ??? very strange ?!
You need to add
#Override
public String isMockEndpoints() {
return "*";
}
This should mock all the enpoints and then you can use mock:file:out for example
If I am not misstaken you are mocking your output endpoint yet your endpoint endpoint is a file endpoint. When you send a message you need to drop a message to whereever the file endpoint is polling. Otherwise you need to mock that as well.

Apache-Camel Accessing header values within string-template

my problem is, I don't know how I can access exchange's header values inside a string-template declaration. I would like to have internationalized mail templates. The test code below ...
public class StringTemplateTest extends CamelTestSupport {
#EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Test
public void testTemplating() throws Exception {
resultEndpoint.expectedBodiesReceived("test");
template.sendBodyAndHeader("test", "lang", "de");
resultEndpoint.assertIsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
from("direct:start").to("string-template:mailTemplate_$simple{in.header.lang}.tm").to("mock:result");
}
};
}
}
ends in a ...
java.io.FileNotFoundException: Cannot find resource: mailTemplate_$simple{in.header.lang}.tm in classpath for URI: mailTemplate_$simple{in.header.lang}.tm
I would expect, the string-template is lookig for mailTemplate_de.tm.
Thank you for help in advance!
Your problem is that .to("component:xyz") endpoints are evaluated at the time the route is built - they are not dynamic and won't pick up ${} properties.
Instead you need to use recipientList, like this:
from("direct:start")
.recipientList(simple("string_template:mailTemplate_${in.header.lang}.tm"))
.to("mock:result")

Camel error thrown while working with interceptSendToEndpoint

While working with the interceptSendToEndpoint, below route throws org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://result]. Exchange[Message: ]
How could I resolve it? Thanks in advance.
public class SampleRouteTest extends CamelTestSupport {
#Test
public void test() {
String expectedBody = "<matched/>";
template.sendBodyAndHeader("direct:start", expectedBody, "foo", "bar");
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
interceptSendToEndpoint("direct:result").process(exchange -> System.out.println("intercepted"));
from("direct:start").to("direct:result").process(exchange -> System.out.println("after"));
}
};
}
}
You need a consumer on "direct:result", eg a route with
from("direct:result")
.to("log:result")
Or something. Or instead of direct use a mock / seda or other component.
The direct component is for direct method invocation, eg there must be a link between to->from

Resources