I have a Camel route which is waiting on a lock. Let's simulate it as following:
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("timer://foo?period=100")
.to("direct:bar");
from("direct:bar")
.routeId("test")
.to("log:receive")
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
System.out.println("sleeping indefinitely");
try {
Thread.sleep(Long.MAX_VALUE);
} catch (final InterruptedException exception) {
exception.printStackTrace();
}
System.out.println("not sleeping indefinitely");
}
});
}
});
At a certain point in time, I want to stop this test route as soon as possible. Is there a way to stop this route while interrupting the thread currently processing this route? I tried stopRoute as below, but this does not seem to interrupt the thread.
context.getRouteController().stopRoute("test", 1, TimeUnit.NANOSECONDS);
I am using Camel version 3.4.x.
Related
I am trying Camel-Kafka integration.
I have two queues :
queue1 and queue2.
There are three routes :
Route1 puts a list of two messages in queue1 (It should do it only once).
Route2 reads the list from queue1, splits it, and puts the individual messages in queue2
Route3 reads the messages from queue2 and just prints it.
The code is as follows :
import java.util.ArrayList;
import java.util.List;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
public class CamelListTest {
public static void main(String[] args) throws Exception {
CamelContext context = new DefaultCamelContext();
context.addRoutes(new CamelListRoute());
context.start();
Thread.sleep(30000);
context.stop();
}
}
class CamelListRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
//Route1, expected to run once
from("timer://timerName?repeatCount=1").process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
List<String> inOrderList = new ArrayList<String>();
inOrderList.add("1");
inOrderList.add("2");
exchange.getIn().setBody(inOrderList, ArrayList.class);
}
})
.to("kafka:<ip>:9092?topic=queue1");
//Route2
from("kafka:<ip>:9092?topic=queue1&groupId=testing&autoOffsetReset=latest&consumersCount=1")
.split()
.body().process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("2nd Route : " + (exchange.getIn().getBody().toString()));
}
})
.to("kafka:<ip>:9092?topic=queue2");
//Route3
from("kafka:<ip>:9092?topic=queue2&groupId=testing&autoOffsetReset=latest&consumersCount=1")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("3rd Route : " + (exchange.getIn().getBody().toString()));
}
});
}
}
It is not working as expected, and there are few issues observed :
The first route, which is expected to run only once (repeatCount=1), runs continuously, putting the same message in queue1 again and again.
The second route reads the messages from queue1, splits it, but does not put it in queue2
Since second route does not put anything in queue2, this route does not get any messages.
Can anyone help me figure out what is wrong here?
I see couple of things:
I hope you are giving Kafka Url like this: "kafka://localhost:9092?topic=queue1"
note: kafka://
Providing zookeeper urls for consumers eg: kafka://?topic=queue1&zookeeperConnect=&consumerStreams=1&groupId=testing&autoOffsetReset=largest
Note in previous point autoOffsetReset value will be largest or smallest instead of latest.
I think you shoud exchange the message.
in processor do something like:
exchng.getOut().setHeader("type", "queue");
exchng.getOut().setBody(exchng.getIn().getBody() );
then could add a choice in the second route, does not require the third route.
I believe the first issue of running continuously and putting the same message in queue1 again and again is happening because you are using the same consumer groupId, groupId=testing for both your kafka consumers in routes 2 and 3.
Amend the kafka consumers to consume from different groupIds like so and this will ensure that the message is not consumed again and again.
//Route2
from("kafka:<ip>:9092?topic=queue1&groupId=testing-queue1&autoOffsetReset=latest&consumersCount=1")
and
//Route3
from("kafka:<ip>:9092?topic=queue2&groupId=testing-queue2&autoOffsetReset=latest&consumersCount=1")
The other issues of producing to queue2 and consuming from it to print, I think might be due to version incompatibilities. I have used camel-kafka version 2.20.1 (that uses kafka-clients 0.11.0.1 under the hood) and 2.21.0 (that uses kafka-clients 1.0.0 under the hood) and changed the routes to reflect the changes like so and this seems to consume-produce-consume fine.
class CamelListRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
//Route1, expected to run once
from("timer://timerName?repeatCount=1").process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
List<String> inOrderList = new ArrayList<String>();
inOrderList.add("1");
inOrderList.add("2");
exchange.getIn().setBody(inOrderList, ArrayList.class);
}
})
.to("kafka:queue1?brokers=<ip>:9092");
//Route2
from("kafka:queue1?brokers=<ip>:9092&groupId=testing-queue1&autoOffsetReset=latest&consumersCount=1")
.split()
.body().process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("2nd Route : " + (exchange.getIn().getBody().toString()));
}
})
.to("kafka:queue2?brokers=<ip>:9092");
//Route3
from("kafka:queue2?brokers=<ip>:9092&groupId=testing-queue2&autoOffsetReset=latest&consumersCount=1")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("3rd Route : " + (exchange.getIn().getBody().toString()));
}
});
}
}
When I send a message to the topic, why the processor get 10 exchanges? This is my code.
public class RouteTest extends RouteBuilder {
public void configure() throws Exception {
from("activemq:topic:Topic.ansyncMessage").process(new Processor() {
public void process(Exchange exchange) throws Exception {
System.out.println(exchange.getIn().getBody());
}
});
}
}
You have a typo 'ansyc' -> 'async'
I've got route that calls cxfbean:
.to("cxfbean:reservationService")
Tried mock this in my test with
#EndpointInject(uri = "mock:reservationService")
MockEndpoint reservationSystemMock;
#BeforeMethod
private void setUpContext() throws Exception
{
context.getRouteDefinition( "send.to.res.svc.endpoint" ).adviceWith(
context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception
{
interceptSendToEndpoint("cxfbean:reservationService")
.skipSendToOriginalEndpoint()
.to("mock:reservationService");
}
});
}
Also tried mock with weaveByToString( "**reservationService" ).replace().to( "mock:reservationService" );. In both cases I get:
Caused by: org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: reservationService
I'd like to test my route without cxf bean instantiation. I'm using CamelTestSupport class as parent.
Managed to mock cxfbean endpoint with weaveByToString( "To[cxfbean:reservationService]" ):
#EndpointInject(uri = "mock:reservationService")
protected MockEndpoint reservationSystemMock;
#BeforeMethod
private void setUpContext() throws Exception
{
context.getRouteDefinition( "send.to.res.svc.endpoint" ).adviceWith(
context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveByToString( "To[cxfbean:reservationService]" )
.replace().to( "mock:reservationService" );
}
});
}
Also seems that we can peek necessary expression for weaveByToString using context.getRouteDefinitions().get(0).toString() in debug watcher
Remember to turn on advice-with on your test class. If you use those annotations, then add #UseAdviceWith to the class.
And then start the camel context after you have advised, which
http://camel.apache.org/spring-testing.html
http://camel.apache.org/advicewith.html
I'm trying to use the RecipientList pattern in Camel but I think I may be missing the point. The following code only displays one entry to the screen:
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
from("direct:start").recipientList(bean(MyBean.class, "buildEndpoint"))
.streaming()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println(exchange.getExchangeId());
}
});
}
};
}
public static class MyBean {
public static String[] buildEndpoint() {
return new String[] { "exec:ls?args=-la", "exec:find?args=."};
}
}
I also tried just returning a comma-delimited string from the buildEndpoint() method and using tokenize(",") in the expression of the recipientList() component definition but I still got the same result. What am I missing?
That is expected, the recipient list sends a copy of the same message to X recipients. The processor you do afterwards is doing after the recipient lists is done, and therefore is only executed once.
During the processing of an Exchange received from JMS I'm creating dynamically a route that fetches a file from FTP to the file system and when the batch is done I need to remove that same route. The following code fragment shows how I do this:
public void execute() {
try {
context.addRoutes(createFetchIndexRoute(routeId()));
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
private RouteBuilder createFetchIndexRoute(final String routeId) {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("ftp://" + getRemoteQuarterDirectory() +
"?fileName=" + location.getFileName() +
"&binary=true" +
"&localWorkDirectory=" + localWorkDirectory)
.to("file://" + getLocalQuarterDirectory())
.process(new Processor() {
RouteTerminator terminator;
#Override
public void process(Exchange exchange) throws Exception {
if (camelBatchComplete(exchange)) {
terminator = new RouteTerminator(routeId,
exchange.getContext());
terminator.start();
}
}
})
.routeId(routeId);
}
};
}
I'm Using a thread to stop a route from a route, which is an approach recommended in the Camel Documentation - How can I stop a route from a route
public class RouteTerminator extends Thread {
private String routeId;
private CamelContext camelContext;
public RouteTerminator(String routeId, CamelContext camelContext) {
this.routeId = routeId;
this.camelContext = camelContext;
}
#Override
public void run() {
try {
camelContext.stopRoute(routeId);
camelContext.removeRoute(routeId);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
}
In result the route does stop. But what I see in the jconsole is that the thread that corresponds to the route isn't removed. Thus in time these abandoned threads just keep accumulating.
Is there a way to properly stop/remove a route dynamically/programmatically and also to release the route's thread, so that they don't accumulate through time?
This is fixed in the next Camel release 2.9.2 and 2.10. Fixed by this ticket:
https://issues.apache.org/jira/browse/CAMEL-5072