Apache Camel processing with a substring of the message - apache-camel

I want to build a Camel route that do 3 HttpRequests but in every request it should only a part of the body.
In my Message Body is this XML:
<part1>
...
</part1>
<part2>
...
</part2>
<part3>
...
</part3>
Now every Part should send to a REST-Service. The Rest-Service response with some data, this data I must put in the body of the next part. How can I solve this?
The route should like this:
from("activemq:inMsg")
.setBody(xpath("//part1")).inOut("http4://localhost/workingPart1")
.choice()
.when()
.replay().isEqual("ok")
.setBody("<responsePart1>"+replay()+"</responsePart1>" + xpath("//part2")).inOut("http4://localhost/workingPart2")
.choice()
.when()
.replay().isEqual("ok")
.setBody("<responsePart2>"+replay()+"</responsePart2>" + xpath("//part3")).inOut("http4://localhost/workingPart3")
.otherwiese().to("activemq:error")
.end()
.otherwiese().to("activemq:error")
.end()
Can you help me to find the right syntax?
Thank you

I guess there is more than one possible solution for that:
ModelCamelContext context = new DefaultCamelContext();
context.addRoutes(new RestBuilder("direct:rest1", "http4://localhost/workingPart1"));
context.addRoutes(new RestBuilder("direct:rest2", "http4://localhost/workingPart2"));
context.addRoutes(new RestBuilder("direct:rest3", "http4://localhost/workingPart3"));
context.addRoutes(new MainRouteBuilder());
ProducerTemplate template = context.createProducerTemplate();
template.sendBody("direct:start", XML);
The RestBuilder class:
public class RestBuilder extends RouteBuilder {
private final String endpoint;
private final String uri;
public RestBuilder(final String endpoint, final String uri) {
this.endpoint = endpoint;
this.uri = uri;
}
#Override
public void configure() {
from(endpoint)
.setHeader(Exchange.HTTP_METHOD, constant(org.apache.camel.component.http4.HttpMethods.POST))
.to(uri)
.choice()
.when(header(Exchange.HTTP_RESPONSE_CODE).isNotEqualTo(Integer.valueOf(200)))
.throwException(new IllegalStateException());
}
}
The MainRouteBuilder class:
public class MainRouteBuilder extends RouteBuilder {
#Override
public void configure() {
onException(IllegalStateException.class)
.log("Hm, no good")
.to("activemq:error")
.handled(true);
from("direct:start")
.setProperty("part1").xpath("/parts/part1/*")
.setProperty("part2").xpath("/parts/part2/*")
.setProperty("part3").xpath("/parts/part3/*")
.setBody(simple("${property.part1}"))
.to("direct:rest1")
.setBody(simple("<responsePart1>${body}</responsePart1>${property.part2}"))
.to("direct:rest2")
.setBody(simple("<responsePart2>${body}</responsePart2>${property.part3}"))
.to("direct:rest3");
}
}

Related

How to write the unit tests for the apache camel routes

Can someone please help in writing the unit tests for this class.
==============================================================================================
#Component("edi820AdapterRouteBuilder")
public class EDI820AdapterRouteBuilder extends BaseRouteBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(EDI820AdapterRouteBuilder.class);
private static final String DIRECT_Q_RECEIVER = "direct:queueReceiver";
private static final String DIRECT_PROCESS_820 = "direct:process820";
private static final String DIRECT_TO_HIX = "direct:toHIX";
private static final String DIRECT_TO_CMS = "direct:toCMS";
private static final String MDC_TRANSACTIONID = "transactionId";
private static final String REQUEST_ID = "RequestID";
private static final String DIRECT_PUT_MDC = "direct:putMDC";
private static final String DIRECT_REMOVE_MDC = "direct:removeMDC";
#Override
public void configure() throws Exception {
super.configure();
LOGGER.debug("configure called.");
String queueName = appConfig.getInboundQueueName();
LOGGER.debug("inboundQueueName: {}", queueName);
String toHIXendpoint = appConfig.getEndpointWithOptions("toHIXendpoint");
LOGGER.debug("toHIXendpoint: {}", toHIXendpoint);
String toCMSendpoint = appConfig.getEndpointWithOptions("toCMSendpoint");
LOGGER.debug("toCMSendpoint: {}", toCMSendpoint);
String routeDelay = appConfig.getRouteDelay();
LOGGER.debug("routeDelay: {}",routeDelay);
from("timer://runOnce?repeatCount=1&delay="+routeDelay)
.to("bean:edi820AdapterRouteBuilder?method=addRoute")
.end();
from(DIRECT_Q_RECEIVER)
.to(PERSIST_EDI820_XDATA)
.to(EDI820_REQUEST_TRANSFORMER)
.to(DIRECT_PROCESS_820)
.log(LoggingLevel.INFO, LOGGER,"Executed "+DIRECT_Q_RECEIVER)
.end();
from(DIRECT_PROCESS_820)
.choice()
.when(header(TRANSACTION_SOURCE_STR).isEqualTo(HIX_SOURCE_SYSTEM))
.log(LoggingLevel.INFO, LOGGER,"Calling route for: "+HIX_SOURCE_SYSTEM)
.to(DIRECT_TO_HIX)
.when(header(TRANSACTION_SOURCE_STR).isEqualTo(CMS_SOURCE_SYSTEM))
.log(LoggingLevel.INFO, LOGGER,"Calling route for: "+CMS_SOURCE_SYSTEM)
.to(DIRECT_TO_CMS)
.otherwise()
.log(LoggingLevel.INFO, LOGGER,"Invalid "+TRANSACTION_SOURCE_STR+" ${header["+TRANSACTION_SOURCE_STR+"]}")
.end();
from(DIRECT_TO_HIX).routeId("edi820adapter-to-hix-producer-route")
.log(LoggingLevel.INFO, LOGGER,"Executing edi820adapter-to-hix-producer-route")
.marshal().json(JsonLibrary.Jackson)//Convert body to json string
.to(toHIXendpoint)
.log(LoggingLevel.DEBUG, LOGGER, "json body sent to edi820-hix: ${body}")
.log(LoggingLevel.INFO, LOGGER,"Executed edi820adapter-to-hix-producer-route")
.end();
from(DIRECT_TO_CMS).routeId("edi820adapter-to-cms-producer-route")
.log(LoggingLevel.INFO, LOGGER,"Executing edi820adapter-to-cms-producer-route")
.marshal().json(JsonLibrary.Jackson)//Convert body to json string
.to(toCMSendpoint)
.log(LoggingLevel.DEBUG, LOGGER, "json body sent to edi820-cms: ${body}")
.log(LoggingLevel.INFO, LOGGER,"Executed edi820adapter-to-cms-producer-route")
.end();
from(DIRECT_PUT_MDC).process(new Processor() {
public void process(Exchange exchange) throws Exception {
if (exchange.getIn().getHeader(REQUEST_ID) != null) {
MDC.put(MDC_TRANSACTIONID, (String) exchange.getIn().getHeader(REQUEST_ID));
}
}
}).end();
from(DIRECT_REMOVE_MDC).process(new Processor() {
public void process(Exchange exchange) throws Exception {
MDC.remove(MDC_TRANSACTIONID);
}
}).end();
}
public void addRoute(Exchange exchange) {
try {
CamelContext context = exchange.getContext();
ModelCamelContext modelContext = context.adapt(ModelCamelContext.class);
modelContext.addRouteDefinition(buildRouteDefinition());
} catch (Exception e) {
LOGGER.error("Exception in addRoute: {}", e.getMessage());
LOGGER.error(ExceptionUtils.getFullStackTrace(e));
}
}
private RouteDefinition buildRouteDefinition() {
String queueName = appConfig.getInboundQueueName();
RouteDefinition routeDefinition = new RouteDefinition();
routeDefinition
.from("jms:queue:" + queueName).routeId("edi820-adapter-jms-consumer-route")
.to(DIRECT_PUT_MDC)
.log(LoggingLevel.INFO, LOGGER,"Executing edi820-adapter-jms-consumer-route")
.log(LoggingLevel.DEBUG, LOGGER, "Received Message from queue: "+queueName)
.to(DIRECT_Q_RECEIVER)
.log(LoggingLevel.INFO, LOGGER,"Executed edi820-adapter-jms-consumer-route")
.to(DIRECT_REMOVE_MDC)
.end();
return routeDefinition;
}
}
==============================================================================
Please let me know if any additional information is needed. Thanks for the help in advance.
It is not possible to write entire test cases mentioning those classes. So I'm writing snippet code for understanding purposes.
Sample Example for Understanding:
Below is a snippet of code for the route builder class:
public class CheckRouteBuilder extends RouteBuilder{
#Override
public void configure() throws Exception {
from("timer://runOnce?repeatCount=1&delay="+routeDelay)
.to("bean:edi820AdapterRouteBuilder?method=addRoute")
.end();
}
}
Follow the test cases of the above class:
public class CheckRouteBuilderTest extends CamelTestSupport {
#Test
public void testConfigure() throws Exception {
context.addRoutes(new CheckRouteBuilder ());
RouteDefinition route = context.getRouteDefinitions().get(0);
AdviceWith.adviceWith(route, context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:TestEndpoint"); // timer://runOnce?repeatCount=1&delay="+routeDelay replace with direct:TestEndpoint because during test timer component not support that's why need to replace with direct component.
weaveAddLast().to("mock:endPoint");
}
});
MockEndpoint mockEndpoint = getMockEndpoint("mock:endPoint");
mockEndpoint.expectedMessageCount(1);
template.sendBodyAndHeaders("direct:TestEndpoint", "#", "##");
mockEndpoint.assertIsSatisfied();
}
}
# -> Mention body like String, JSONObject, XML. If body is not required then simply pass the null value.
## -> Mention Header like Content-type:application/json to need to pass in Map<String, Object) format.

Apache Camel: pollEnrich does not call dynamic URI

I have a route to poll the email from the server. In the FunctionRoute I am trying to pass the subject of the email I want to fetch by the IMAP protocol using pollEnrich method in the EmailPollingRoute. Currently I am facing problem to make a dynamic URI endpoint. I noticed the headers are being cleared before calling the pollEnrich method. Therefore I tried to use the ${exchangeProperty.subject} but there is no email is fetched. When I set the subject directly in the endpoint as String then the email is being fetched.
How can I set the subject dynamically in the pollEnrich method? The subject is being passed from the FunctionRoute to EmailPollingRoute route.
I appreciate any help
#Component
public class FunctionRoute extends EmailPollingRoute {
static final Logger LOGGER = LoggerFactory.getLogger(FunctionRoute.class);
#Override
public void configure() throws Exception {
from("direct:process_polling_email_function")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
subject = "ABCD - Test1";
exchange.setProperty("ABCD - Test1");
LOGGER.info("1. subject - " + subject);
}})
//.setProperty("subject", constant("ABCD - Test1"))
//.setHeader("subject", simple("ABCD - Test1"))
.to("direct:process_polling_email");
}
}
#Component
public class EmailPollingRoute extends BaseRouteBuilder {
static final Logger LOGGER = LoggerFactory.getLogger(EmailPollingRoute.class);
public String subject;
#Override
public void configure() throws Exception {
//super.configure();
//2362
//#formatter:off
from("direct:process_polling_email")
.routeId("routeId_EmailPollingRoute")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
subject = exchange.getProperty("subject", String.class);
LOGGER.info("0001 - subjectB: (" + subject + ")");
}})
.pollEnrich("imaps://XXXXXXX.info"
+ "?username=XXXXX&"
+ "password=XXXXXXX&"
+"folderName=Inbox/Test&"
+"searchTerm.fromSentDate=now-72h&"
+"searchTerm.subject=${exchangeProperty.subject}&"
+"fetchSize=1");
.to("log:newmail");
//#formatter:on
}
}
You can set pollEnrich URI dynamically using simple method, you can also specify timeout in similar way.
.pollEnrich()
.simple("imaps://XXXXXXX.info"
+ "?username=XXXXX&"
+ "password=XXXXXXX&"
+"folderName=Inbox/Test&"
+"searchTerm.fromSentDate=now-72h&"
+"searchTerm.subject=${exchangeProperty.subject}&"
+"fetchSize=1")
.timeout(1000);

How to browse messages from a queue using Apache Camel?

I need to browse messages from an active mq using Camel route without consuming the messages.
The messages in the JMS queue are to be read(only browsed and not consumed) and moved to a database while ensuring that the original queue remains intact.
public class CamelStarter {
private static CamelContext camelContext;
public static void main(String[] args) throws Exception {
camelContext = new DefaultCamelContext();
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnectionFactory.DEFAULT_BROKER_URL);
camelContext.addComponent("jms", JmsComponent.jmsComponent(connectionFactory));
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("jms:queue:testQueue").to("browse:orderReceived") .to("jms:queue:testQueue1");
}
}
);
camelContext.start();
Thread.sleep(1000);
inspectReceivedOrders();
camelContext.stop();
}
public static void inspectReceivedOrders() {
BrowsableEndpoint browse = camelContext.getEndpoint("browse:orderReceived", BrowsableEndpoint.class);
List<Exchange> exchanges = browse.getExchanges();
System.out.println("Browsing queue: "+ browse.getEndpointUri() + " size: " + exchanges.size());
for (Exchange exchange : exchanges) {
String payload = exchange.getIn().getBody(String.class);
String msgId = exchange.getIn().getHeader("JMSMessageID", String.class);
System.out.println(msgId + "=" +payload);
}
As far as I know, not possible in Camel to read (without consuming !) JMS messages...
The only workaround I found (in a JEE app) was to define a startup EJB with a timer, holding a QueueBrowser, and delegating the msg processing to a Camel route:
#Singleton
#Startup
public class MyQueueBrowser {
private TimerService timerService;
#Resource(mappedName="java:/jms/queue/com.company.myqueue")
private Queue sourceQueue;
#Inject
#JMSConnectionFactory("java:/ConnectionFactory")
private JMSContext jmsContext;
#Inject
#Uri("direct:readMessage")
private ProducerTemplate camelEndpoint;
#PostConstruct
private void init() {
TimerConfig timerConfig = new TimerConfig(null, false);
ScheduleExpression se = new ScheduleExpression().hour("*").minute("*/"+frequencyInMin);
timerService.createCalendarTimer(se, timerConfig);
}
#Timeout
public void scheduledExecution(Timer timer) throws Exception {
QueueBrowser browser = null;
try {
browser = jmsContext.createBrowser(sourceQueue);
Enumeration<Message> msgs = browser.getEnumeration();
while ( msgs.hasMoreElements() ) {
Message jmsMsg = msgs.nextElement();
// + here: read body and/or properties of jmsMsg
camelEndpoint.sendBodyAndHeader(body, myHeaderName, myHeaderValue);
}
} catch (JMSRuntimeException jmsException) {
...
} finally {
browser.close();
}
}
}
Apache camel browse component is exactly designed for that. Check here for the documentation.
Can't say more since you have not provided any other information.
Let's asssume you have a route like this
from("activemq:somequeue).to("bean:someBean")
or
from("activemq:somequeue).process(exchange -> {})
All you got to do it put a browse endpoint in between like this
from("activemq:somequeue).to("browse:someHandler").to("bean:someBean")
Then write a class like this
#Component
public class BrowseQueue {
#Autowired
CamelContext camelContext;
public void inspect() {
BrowsableEndpoint browse = camelContext.getEndpoint("browse:someHandler", BrowsableEndpoint.class);
List<Exchange> exchanges = browse.getExchanges();
for (Exchange exchange : exchanges) {
......
}
}
}

Mock not found in the registry for Camel route test

I am trying to test a Camel route (polling messages from an SQS queue) containing
.bean("messageParserProcessor")
where messageParserProcessor is a Processor.
The test:
public class SomeTest extends CamelTestSupport {
private final String queueName = ...;
private final String producerTemplateUri = "aws-sqs://" + queueName + ...;
private static final String MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT = "mock:messageParserProcessor";
#EndpointInject(uri = MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT)
protected MockEndpoint messageParserProcessor;
#Override
public boolean isUseAdviceWith() {
return true;
}
#Before
public void setUpContext() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("bean:messageParserProcessor")
.skipSendToOriginalEndpoint()
.process(MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT);
}
});
}
#Test
public void testParser() throws Exception {
context.start();
String expectedBody = "test";
messageParserProcessor.expectedBodiesReceived(expectedBody);
ProducerTemplate template = context.createProducerTemplate();
template.sendBody(producerTemplateUri, expectedBody);
messageParserProcessor.assertIsSatisfied();
context.stop();
}
}
When I run the test I get this error:
org.apache.camel.FailedToCreateRouteException:
Failed to create route route1 at:
>>> InterceptSendToEndpoint[bean:messageParserProcessor -> [process[ref:mock:messageParserProcessor]]] <<< in route: Route(route1)[[From[aws-sqs://xxx...
because of No bean could be found in the registry for: mock:messageParserProcessor of type: org.apache.camel.Processor
Same error if I replace interceptSendToEndpoint(...) with mockEndpointsAndSkip("bean:messageParserProcessor")
The test can be executed (but obviously doesn't pass) when I don't use a mock:
interceptSendToEndpoint("bean:messageParserProcessor")
.skipSendToOriginalEndpoint()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {}
});
So the problem is the mock that is not found, what is wrong in the way I create it?
So I found a workaround to retrieve mocks from the registry:
interceptSendToEndpoint("bean:messageParserProcessor")
.skipSendToOriginalEndpoint()
.bean(getMockEndpoint(MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT));
// Instead of
// .process(MESSAGE_PARSER_PROCESSOR_MOCK_ENDPOINT);
But I still don't understand why using .process("mock:someBean") doesn't work...

Doing bean inject in camel test

I have a camel app which look something like below which has a route like below:-
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
The entry point of the code look something like below:
public class OrderMainApp {
public static void main(String... args) throws Exception {
OrderMainApp orderMainApp = new OrderMainApp();
DefaultCamelContext camelContext = new DefaultCamelContext();
ProducerTemplate producer = camelContext.createProducerTemplate();
camelContext.setRegistry(orderMainApp.createRegistry(producer));
camelContext.addRoutes(new OrderRouteBuilder(producer));
camelContext.start();
}
protected JndiRegistry createRegistry(ProducerTemplate producer) throws Exception {
JndiRegistry jndi = new JndiRegistry();
OrderHelper orderHelper = new OrderHelper();
orderHelper.setProducer(producer);
jndi.bind("orderHelper", orderHelper);
return jndi;
}
}
In OrderRouteBuilder configure has routes like below:-
//processor is a custom JSONProcessor extending Processor
from("jetty:http://localhost:8888/orchestratorservice").process(processor);
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
My goal is to test the response I receive from bean:orderHelper?method=getMarketplaceOrders when I place a request on direct:getMarketplaceOrders
orderHelper.getMarketplaceOrders looks like below:-
public OrderResponse getMarketplaceOrders(GetMarketplaceOrdersRequest requestParam) throws Exception
My test class look something like below:-
public class OrderMainAppTest extends CamelTestSupport {
#Produce(uri = "direct:getMarketplaceOrders")
protected ProducerTemplate template;
#EndpointInject(uri = "bean:orderHelper?method=getMarketplaceOrders")
protected MockEndpoint resultEndpoint;
#Test
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
template.sendBody("{\"fromDateTime\": \"2016-01-11 10:12:13\"}");
resultEndpoint.expectedBodiesReceived(expectedBody);
resultEndpoint.assertIsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
}
};
}
}
Whenever I am running the test I am getting the below exception:-
java.lang.IllegalArgumentException: Invalid type: org.apache.camel.component.mock.MockEndpoint which cannot be injected via #EndpointInject/#Produce for: Endpoint[bean://orderHelper?method=getMarketplaceOrders]
I am guessing this is because I am not able to pass on OrderHelper to the camel test context. Can some one let me know how can I inject the bean in the mock result end point?
EDIT:-
I tried modifying my test class as follows:-
public class OrderMainAppTest extends CamelTestSupport {
protected OrderHelper orderHelper = new OrderHelper();
#Produce(uri = "direct:getMarketplaceOrders")
protected ProducerTemplate template;
#EndpointInject(uri = "mock:intercepted")
MockEndpoint mockEndpoint;
#Before
public void preSetup() throws Exception {
orderHelper.setProducer(template);
};
#Test
public void testSendMatchingMessage() throws Exception {
GetMarketplaceOrdersRequest request = new GetMarketplaceOrdersRequest();
request.setFromDateTime("2016-01-11 10:12:13");
request.setApikey("secret_key");
request.setMethod("getMarketplaceOrders");
request.setLimit(10);
request.setOffset(2);
template.sendBody(request);
mockEndpoint.expectedBodiesReceived("{\"success\":\"false\"");
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
interceptSendToEndpoint("bean:orderHelper?method=getMarketplaceOrders")
.to("mock:intercepted"); from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
}
};
}
#Override
protected JndiRegistry createRegistry() throws Exception {
return getRegistry();
}
protected JndiRegistry getRegistry() {
JndiRegistry jndi = new JndiRegistry();
jndi.bind("orderHelper", orderHelper);
return jndi;
}
}
The above code is making the request correctly and is flowing through my app correctly. But I am not able to intercept the response of orderHelper.getMarketplaceOrders. The above code is intercepting only the request. I tried changing to template.requestBody(request). But still no luck.
This error means you can't inject a bean: endpoint into a MockEndpoint.
If you want to "intercept" the call into your OrderHelper, you can use interceptSendToEndpoint in your route :
#EndpointInject(uri = "mock:intercepted")
MockEndpoint mockEndpoint;
...
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
interceptSendToEndpoint("bean:orderHelper?method=getMarketplaceOrders")
.to("mock:intercepted");
from("direct:getMarketplaceOrders")
.to("bean:orderHelper?method=getMarketplaceOrders");
}
};
See : http://camel.apache.org/intercept.html
By updating my createRouteBuilder as shown below. I am able to intercept the response and send it to a mock endpoint where I can do the assertion.
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders").onCompletion()
.to("mock:intercepted");
}
};
}

Resources