My project is working on getting data from one system to another. We are using Apache Camel Routes to send the data between JBoss EAP v7 servers. My question is, is there a way to investigate what the content of the packages are as they come across different routes?
We have tried upping the logging but our files/console just get flooded. We have also tried to use Hawtio on the server to see the messages coming across the routes but have had no success identifying where our message is getting "stuck".
Any help is appreciated!
You can use unit tests to test your routes locally and then either log contents of the exchange at specific points using adviceWith and weave methods.
With unit tests you can easily debug your routes in your favourite IDE even if you're running camel in something like Karaf or Red Hat fuse.
package com.example;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.model.dataformat.JsonLibrary;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class ExampleRouteTests extends CamelTestSupport {
#Test
public void exampleTest() throws Exception
{
ContractDetails testDetails = new ContractDetails(1512, 1215);
mockJDBCEndpoints();
context.getRouteDefinition("exampleRoute")
.adviceWith(context, new AdviceWithRouteBuilder(){
#Override
public void configure() throws Exception {
replaceFromWith("direct:start");
weaveByToUri("direct:getDetailsFromAPI")
.replace()
.to("log:testLogger?showAll=true")
.to("mock:api")
.setBody(constant(testDetails));
weaveByToUri("direct:saveToDatabase")
.replace()
.to("log:testLogger?showAll=true")
.to("mock:db");
}
});
MockEndpoint apiMockEndpoint = getMockEndpoint("mock:api");
apiMockEndpoint.expectedMessageCount(1);
MockEndpoint dbMockEndpoint = getMockEndpoint("mock:db");
dbMockEndpoint.expectedMessageCount(1);
context.start();
String body = "{\"name\":\"Bob\",\"age\":10}";
template.sendBody("direct:start", body);
apiMockEndpoint.assertIsSatisfied();
dbMockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder(){
#Override
public void configure() throws Exception {
from("amqp:queue:example")
.routeId("exampleRoute")
.unmarshal().json(JsonLibrary.Jackson,
Person.class)
.to("direct:getDetailsFromAPI")
.process(new SomeProcessor())
.to("direct:saveToDatabase");
from("direct:saveToDatabase")
.routeId("saveToDatabaseRoute")
.to("velocity:sql/insertQueryTemplate.vt")
.to("jdbc:exampleDatabase");
from("direct:getDetailsFromAPI")
.removeHeaders("*")
.toD("http4:someAPI?name=${body.getName()}")
.unmarshal().json(JsonLibrary.Jackson,
ContractDetails.class);
}
};
}
void mockJDBCEndpoints() throws Exception {
context.getRouteDefinition("saveToDatabaseRoute")
.adviceWith(context, new AdviceWithRouteBuilder(){
#Override
public void configure() throws Exception {
weaveByToUri("jdbc:*")
.replace()
.to("mock:db");
}
});
}
#Override
public boolean isUseAdviceWith() {
return true;
}
}
Now for troubleshooting problems that do not occur with unit tests you can configure generic or route specific exception handling with onException and use Dead letter channel to process and and store information about the failed exchange. Alternatively you can just use stream or file component to save information about the exception and failed exchange in to a separate file to avoid flooding logs.
Related
We are using apache camel to 2.17.* version inorder to utilize the maxPollRecords parameter I am trying to upgrade to 2.18.0. Post upgrade to 2.18.0 The consumer doesn't seems to be recognized by the broker anymore. Following is the sample consumer I tried to create. I could produce the message from cli to the topic and if I create a consumer in the cli I could see the consumer created in cli consumes the message but not the consumer created through apache camel.
Also with the consumer group describe cli command I could see the consumer-id as blank if I run only apache camel consumer instance. While I was running with 2.17.5 the broker used to recognize and assign that to the partition. I can't find the example
please help.
package com.test;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.properties.PropertiesComponent;
import org.apache.camel.impl.DefaultCamelContext;
public class CamelConsumer {
public static void main(String argv[]){
CamelContext camelContext = new DefaultCamelContext();
// Add route to send messages to Kafka
try {
camelContext.addRoutes(new RouteBuilder() {
public void configure() {
PropertiesComponent pc = getContext().getComponent("properties", PropertiesComponent.class);
pc.setLocation("classpath:application.properties");
System.out.println("About to start route: Kafka Server -> Log ");
from("kafka:{{consumer.topic}}?brokers={{kafka.host}}:{{kafka.port}}"
+ "&maxPollRecords={{consumer.maxPollRecords}}" + "&consumersCount={{consumer.consumersCount}}"
+ "&groupId={{consumer.group}}").routeId("FromKafka")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Message message = exchange.getIn();
Object data = message.getBody();
System.out.println(data);
}
});
}
});
camelContext.start();
Thread.sleep(5 * 60 * 1000);
camelContext.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I donot get any exception either. I donot find any documentation related to this. Please help.
consumer.topic=test kafka.host=localhost kafka.port=9092
consumer.maxPollRecords=1 consumer.consumersCount=1
consumer.group=test
I could find out the working example code from 2.19* in the following repository.
https://github.com/Talend/apache-camel/branches (Forked branch)
https://github.com/apache/camel (actual camel branch)
Finally it worked with the version 2.21.5 and I had to bump up the apache kafka maven version to 1.0.0 from 0.9*
I have been inventing a way how to work around the problem of adding consumers to a jetty endpoint (it does not allow multiple consumers). The way we do it in our company is to build our own router and a broadcasting endpoint which consumes from jetty and routes requests to underlying "subscriptions". Only one of them will eventually process the request. It kind of works but it's not completely ok, since recently when updating to latest Camel we have found our custom built component to leak memory and in general I consider using built-in functionality over custom hacks.
I started investigating the Camel REST API and found it very nice and pretty much replacing our home-grown component apart from one thing - you cannot re-configure it at runtime - you have to stop the context basically for this to work. Below I include my unit test with a happy path and the path that fails. Frankly I think is a bug, but if there is a legitimate way to achieve what I want, I'd like to hear sound advice:
package com.anydoby.camel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Test;
/**
* Test tries to add/remove routes at runtime.
*/
public class RoutesTest {
private DefaultCamelContext ctx;
#Before
public void pre() throws Exception {
ctx = new DefaultCamelContext();
new RouteBuilder(ctx) {
#Override
public void configure() throws Exception {
restConfiguration("jetty").host("localhost").port(8080);
rest("/")
.get("/issues/{isin}").route().id("issues")
.process(e -> e.getOut().setBody("Here's your issue " + e.getIn().getHeader("isin"))).endRest()
.get("/listings").route().id("listings").process(e -> e.getOut().setBody("some listings"));
}
}.addRoutesToCamelContext(ctx);
ctx.start();
}
#Test
public void test() throws IOException {
{
InputStream stream = new URL("http://localhost:8080/issues/35").openStream();
assertEquals("Here's your issue 35", IOUtils.toString(stream));
}
{
InputStream stream = new URL("http://localhost:8080/listings").openStream();
assertEquals("some listings", IOUtils.toString(stream));
}
}
#Test
public void disableRoute() throws Exception {
ctx.stopRoute("issues");
ctx.removeRoute("issues");
try (InputStream stream = new URL("http://localhost:8080/issues/35").openStream()) {
fail();
} catch (Exception e) {
}
new RouteBuilder(ctx) {
#Override
public void configure() throws Exception {
rest().get("/issues/{isin}/{sedol}").route().id("issues")
.process(e -> e.getOut()
.setBody("Here's your issue " + e.getIn().getHeader("isin") + ":" + e.getIn().getHeader("sedol")))
.endRest();
}
}.addRoutesToCamelContext(ctx);
{
InputStream stream = new URL("http://localhost:8080/issues/35/65").openStream();
assertEquals("Here's your issue 35:65", IOUtils.toString(stream));
}
}
}
The disableRoute() test fails since I cannot add another consumer to an existing endpoint.
So my question is - "is there a way to add a new URL mapping to a restful camel-jetty endpoint"? If you do it during first configuration it works fine, but when later you want to reconfigure one of the routes the error is:
org.apache.camel.FailedToStartRouteException: Failed to start route because of Multiple consumers for the same endpoint is not allowed: jetty:http://localhost:8080/issues/%7Bisin%7D/%7Bsedol%7D?httpMethodRestrict=GET
I'm using camel 2.15.1 and I'm trying to use adviceWith() but I keep getting deprecation warnings. Below is the relevant snippet:
routeDefinition.adviceWith(camelContext, new AdviceWithRouteBuilder(){
#Override
public void configure() throws Exception {
interceptSendToEndpoint("direct:doSomething")
.skipSendToOriginalEndpoint()
}
});
I know I can avoid the deprecation warnings by casting camelContext to ModelCamelContext but casting like that has a bit of a smell to it. Is casting the right way to handle it?
https://camel.apache.org/advicewith.html
Those deprecatations has been removed in future release as we really just want people to access all what they need from CamelContext.
But it has an adapt method so you can adapt to the type without type casting
ModelCamelContext mcc = context.adapt(ModelCamelContext.class);
This solved the problem for my unit tests:
public void testMe() throws Exception{
CamelContext context = this.context();
// Get the route that we're after by ID.
RouteDefinition route = context.getRouteDefinition("<routeID>");
//override the default send endpoint.
route.adviceWith(context.adapt(ModelCamelContext.class), new RouteBuilder() {
public void configure() throws Exception {
interceptSendToEndpoint("mock:overrideme")
.skipSendToOriginalEndpoint()
.to("mock:inbound");
}
});
}
for camel version change 2 to 3, here what you can go for regarding the RouteDefination and AdviceWith , ModelCamelContext, CamelContext
`private void methodName() throws Exception {
AdviceWith.adviceWith(context, "routeId" , route -> {
route.interceptSendToEndpoint("direct:doSomething").skipSendToOriginalEndpoint();
});
context.start();
}`
The adviceWith method was moved as explained here.
AdviceWithRouteBuilder was also used in this example, which I believe is an example of this question. For now, in 2022 I think it could be better represented in the following:
package TestExamples.AdviceExamples;
import org.apache.camel.EndpointInject;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.AdviceWith;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit5.CamelTestSupport;
import org.junit.jupiter.api.Test;
/**
* Unit test demonstrating various functionality of using advice-with
*/
public class AdviceWithMockEndpointsTest extends CamelTestSupport {
#EndpointInject("mock:seda:camel")
protected MockEndpoint camelPoint;
#EndpointInject("mock:seda:other")
protected MockEndpoint otherPoint;
#Test
public void testMockEndpoints() throws Exception {
// must start Camel after we are done using advice-with
AdviceWith.adviceWith(context, "quotes", routeBuilder ->{
routeBuilder.weaveByToUri("seda:camel").replace().to(camelPoint);
routeBuilder.weaveByToUri("seda:other").replace().to(otherPoint);
});
context.start();
camelPoint.expectedBodiesReceived("Camel rocks");
otherPoint.expectedBodiesReceived("Bad donkey");
template.sendBody("seda:quotes", "Camel rocks");
template.sendBody("seda:quotes", "Bad donkey");
assertMockEndpointsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("seda:quotes").routeId("quotes")
.choice()
.when(simple("${body} contains 'Camel'"))
.to("seda:camel")
.otherwise()
.to("seda:other");
}
};
}
}
I recently started to investigate Apache Camel and I have one issue.
I start writing some test for my routes, and there are a lot of examples, where "to" part of route is written as
<route id="person-add-route">
<from uri="direct:start"/>
<to uri="mock:result"/>
</route>
So, I wrote a test, where I am exepcting to have mock:result as last endproint.
#Test
#DirtiesContext
public void testCamel() throws Exception {
// Given
Object body = "body";
int messageCount = 1;
MockEndpoint endpoint = getMockEndpoint("mock:result");
// When
template.sendBody("direct:start", body);
// Then
endpoint.expectedMessageCount(messageCount);
endpoint.assertIsSatisfied();
}
Here is the questions: Is this important to write mock:result if I want to test my route??
You don't need to include "mock:result" in production, there are multiple ways to test your route. One is to implement isMockEndpoints in your Camel test:
#Override
public String isMockEndpoints()
{
return "*";
}
So if your route is like this:
<route id="person-add-route">
<from uri="direct:start"/>
<to uri="direct:result"/>
</route>
You can check the MockEndpoint like this:
MockEndpoint endpoint = getMockEndpoint("mock:direct:result");
You can also use AdviceWith to modify your route at test time, by doing something like this:
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception
{
weaveAddLast().to("mock:result");
}
});
Also, as Claus mentioned in his comment, make sure you set your expectations before you send your message to the route.
The above (and currently accepted) answer is very old and probably not very accurate anymore. To implement a similar approach today could look something like this:
Given a route defined as a spring component:
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
import java.util.Map;
#Component
public class MyRoute extends RouteBuilder {
#Override
public void configure() {
from("direct:start").routeId("myRoute")
.setBody().simple("${body} world")
.process(exchange ->
exchange.getIn().setHeaders(Map.of("foo", "bar")));
}
}
Then the route can be tested as follows:
import org.apache.camel.CamelContext;
import org.apache.camel.EndpointInject;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import static org.apache.camel.builder.AdviceWith.adviceWith;
import static org.assertj.core.api.Assertions.assertThat;
#SpringBootTest
#CamelSpringBootTest
#DirtiesContext
class MyRouteTest {
#Autowired
protected CamelContext camelContext;
#Produce("direct:start")
private ProducerTemplate from;
#EndpointInject("mock:result")
private MockEndpoint mockEndpoint;
#Test
void testMyRoute() throws Exception {
// arrange
adviceWith(camelContext, "myRoute",
route -> route.weaveAddLast().to("mock:result"));
mockEndpoint.expectedMessageCount(1);
// act
from.sendBody("hello");
// assert
mockEndpoint.assertIsSatisfied();
var result = mockEndpoint.getExchanges().get(0).getIn();
assertThat(result.getBody()).isEqualTo("hello world");
assertThat(result.getHeader("foo")).isEqualTo("bar");
}
}
So what route.weaveAddLast().to("mock:result") basically does, is weaving a last step to your route that will redirect the message to mock:result.
I'm designing a Java based MongoDB app and I've ran into a snag when working with Spark.
package com.tengen;
import spark.Request;
import spark.Response;
import spark.Route;
import spark.Spark;
public class HelloWorldSparkStyle {
public static void main(String[] args) {
Spark.get(new Route("/") {
#Override
public Object handle(Request request, Response response) {
return "Hello World From Spark";
}
});
}
}
On new Route("/") I get the error Route() in route cannot be applied to java.lang.string.
I'm confused as to why this doesn't work as I've followed their code exactly.
This should probably be posted on the MongoDB class forum, but I ran into a similar issue. Looks like the get method changed from when the course material was produced. The get now requires a path and a Route
get(path, Route)
import spark.Request;
import spark.Response;
import spark.Route;
import spark.Spark;
public class HelloWorldSparkStyle {
public static void main(String[] args){
Spark.get("/", new Route() {
public Object handle(final Request request, final Response response){
return "Hello World from Spark";
}
});
}
}
Actually I used spark-core-1.1.1.jar it worked fine for me, may be the newer versions of Spark(version 2.0.0) support some different syntax.So if you are using the latest version then you can follow the example given by Mike or just add spark-core-1.1.1.jar to your classpath your example will work fine
That would work in Spark 1. In Spark 2 is recommended by Spark the following (source: http://sparkjava.com/news.html):
import static spark.Spark.*;
public class HelloWorld {
public static void main(String[] args) {
get("/", (req, res) -> "Hello World From Spark");
}
}
Currently, Implementation of Spark Java framework needs to use directory out of Route. So you have to correct your code as follow:
public static void main(String[] args) {
spark.Spark.port(PortNumber);
Spark.get("/", new Route() {
public Object handle(Request request, Response response) throws Exception {
return "This is a sample page";
}
});
}
actually, "/" is the resource to your program. And if you want to change the default spark.Spark.port(PortNumber).
Change the version of Spark in the POM file from the exercise files you download from handout. That fixed issue for me.