How to create ConsumerTemplate in apache camel - apache-camel

I have camel processor which reads a file from directory using ConsumerTemplate as shown below
#Component
public class FileReaderProcessor implements Processor {
#Consume
private ConsumerTemplate consumerTemplate;
/**
* {#inheritDoc}
*/
#Override
public void process(Exchange exchange) throws Exception {
final byte[] filecontent = consumerTemplate.receiveBody(SOURCE_FILE_COMPONENT,
byte[].class);
}
}
However when i tried below code it works
final byte[] filecontent = exchange.getContext().createConsumerTemplate().receiveBody(SOURCE_FILE_COMPONENT,
byte[].class);
The problem is i need to annotate the ConsumerTemplate and use it my code. kindly help me to use ConsumerTemplate with annotation
#Consume
private ConsumerTemplate consumerTemplate;

If you check ConsumerTemplate class you will notice that it extends Service. That means it can be Autowired as any other component in Spring Boot, which I am guessing you are using based on #Component annotation used in your code. So the solution is pretty simple:
#Autowired
private ConsumerTemplate consumerTemplate;

Related

Using Project Reactor with Apache Camel

I'd like to know if it is possible to use Project Reactor with Apache Camel, so applications be fully reactive and non-blocking IO. I'd like to know how does the Project Reactor support works when integrating other Apache Camel's components.
Can I read for example from S3 reactively (therefore I'll need to use the Async S3 client behind the scenes)? Or will I block when reading from S3 and then just create a Flux out of what has been returned?
Where reactiveness is needed, you should use the relevant spring and reactor libraries. there are pseudo camel code also u can db call in camel bean or processors etc
#RestController
#RequestMapping(value = "/api/books")
#RequiredArgsContructor
public class HomeController {
private final BookRepository bookRepository;
private final ProducerTemplate template
#GetMapping("")
public Flux<Book> getHome() {
List <Book> books=bookRepository.findAll();
X ret = template.requestBody("direct:something", books, X.class);
}
}
#Component
public class SomeRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:something")
.process(e-> {
List<Book> books = e.getIn.getBody(List.class)
// some logic
e.getIn.setBody(new X( ))
})
}

Camel - Passing specific parameters from routes to processor

I'm trying to setup a route that process files, here is my route configuration:
#Component
public class MyRoute extends RouteBuilder {
public static final String FIRST_ROUTE = "firstRoute";
public static final String SECOND_ROUTE = "secondRoute";
private final Processor myProcessor;
#Autowired
public MyRoute (#Qualifier("my.processor") Processor myProcessor) {
this.myProcessor= myProcessor;
}
#Override
public void configure() throws Exception {
from("file://{{data.input.dir}}?moveFailed=errors&delete=true&doneFileName=ACK-${file:name}&include=MY-FILE-FIRST.*")
.routeId(FIRST_ROUTE)
.process(myProcessor);
from("file://{{data.input.dir}}?moveFailed=errors&delete=true&doneFileName=ACK-${file:name}&include=MY-FILE-SECOND.*")
.routeId(SECOND_ROUTE)
.process(myProcessor);
}
}
As you can see I have two route for two files that have different name (one with FIRST, one with SECOND).
I want to trigger the process with variable to avoid to check the filename inside the process.
Currently my process function look like that:
public void process(Exchange exchange) throws Exception
What I want is something like that:
public void process(Exchange exchange, String identifier) throws Exception
And have the identifier set in the route definition (it's static, not depending on the filename, I need to have "FIRST" for the first route, and "SECOND" for the second one).
Is this possible ?
Because a process interface signature only takes an Exchange parameter, we cannot do process(Exchange exchange, String identifier).
Instead we can create a bean and call the method from the route like below.
from("file://{{data.input.dir}}?moveFailed=errors&delete=true&doneFileName=ACK-${file:name}&include=MY-FILE-SECOND.*")
.routeId(SECOND_ROUTE)
.bean(myBean,"process(*, " + SECOND + ")");

Camel - Enrich CSV from FTP with CSV from local disk using Camel Bindy

The goal is to produce a report every hour by comparing two CSV files with
use of Camel 3.0.0. One is located on a FTP server, the other on disk. How to use poll enrich pattern in combination with unmarshalling the CSV on disk with Bindy Dataformat?
Example code (for simplicity the FTP endpoint is replaced by a file endpoint):
#Component
public class EnricherRoute extends RouteBuilder {
#Override
public void configure() {
from("file://data?fileName=part_1.csv&scheduler=quartz2&scheduler.cron=0+0+0/1+*+*+?")
.unmarshal().bindy(BindyType.Csv, Record.class)
.pollEnrich("file://data?fileName=part_2.csv", new ReportAggregationStrategy())
.marshal().bindy(BindyType.Csv, Record.class)
.to("file://reports?fileName=report_${date:now:yyyyMMdd}.csv");
}
}
The problem in this example is that in the ReportAggregationStrategy the resource (coming from data/part_2.csv, see below) is not unmarshalled. How to unmarshal data/part_2.csv as well?
public class ReportAggregationStrategy implements AggregationStrategy {
#Override
public Exchange aggregate(Exchange original, Exchange resource) {
final List<Record> originalRecords = original.getIn().getBody(List.class);
final List<Record> resourceRecords = resource.getIn().getBody(List.class); // Results in errors!
...
}
}
You can wrap enrichment with direct endpoint and do unmarshaling there.
from("file://data?fileName=part_1.csv&scheduler=quartz2&scheduler.cron=0+0+0/1+*+*+?")
.unmarshal().bindy(BindyType.Csv, Record.class)
.enrich("direct:enrich_record", new ReportAggregationStrategy())
.marshal().bindy(BindyType.Csv, Record.class)
.to("file://reports?fileName=report_${date:now:yyyyMMdd}.csv");
from("direct:enrich_record")
.pollEnrich("file://data?fileName=part_2.csv")
.unmarshal().bindy(BindyType.Csv, Record.class);

Unit testing with Apache Camel

I want to test below camel route. All the example which i find online has route starting with file, where as in my case i have a spring bean method which is getting called every few minutes and finally message is transformed and moved to jms as well as audit directory.
I am clue less on write test for this route.
All i have currently in my test case is
Mockito.when(tradeService.searchTransaction()).thenReturn(dataWithSingleTransaction);
from("quartz2://tsTimer?cron=0/20+*+8-18+?+*+MON,TUE,WED,THU,FRI+*")
.bean(TradeService.class)
.marshal()
.jacksonxml(true)
.to("jms:queue:out-test")
.to("file:data/test/audit")
.end();
Testing with Apache Camel and Spring-Boot is really easy.
Just do the following (the example below is an abstract example just to give you a hint how you can do it):
Write a Testclass
Use the Spring-Boot Annotations to configure the test class.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
#RunWith(SpringRunner.class)
public class MyRouteTest {
#EndpointInject(uri = "{{sourceEndpoint}}")
private ProducerTemplate sourceEndpoint;
....
public void test() {
// send your body to the endpoint. See other provided methods too.
sourceEndpoint.sendBody([your input]);
}
}
In the src/test/application.properties:
Configure your Camel-Endpoints like the source and the target:
sourceEndpoint=direct:myTestSource
Hints:
It's good not to hardwire your start-Endpoint in the route directly when using spring-boot but to use the application.properties. That way it is easier to mock your endpoints for unit tests because you can change to the direct-Component without changing your source code.
This means instead of:
from("quartz2://tsTimer?cron=0/20+*+8-18+?+*+MON,TUE,WED,THU,FRI+*")
you should write:
from("{{sourceEndpoint}}")
and configure the sourceEndpoint in your application.properties:
sourceEndpoint=quartz2://tsTimer?cron=0/20+*+8-18+?+*+MON,TUE,WED,THU,FRI+*
That way you are also able to use your route for different situations.
Documentation
A good documentation about how to test with spring-boot can be found here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html
For Apache Camel: http://camel.apache.org/testing.html
#the hand of NOD Thanks for your hints, i was going into completely wrong direction. After reading your answer i was able to write the basic test and from this i think i can take it forward.
Appreciate your time, however i see that based on my route it should drop an XML file to audit directory which is not happening.
Look like intermediate steps are also getting mocked, without I specifying anything.
InterceptSendToMockEndpointStrategy - Adviced endpoint [xslt://trans.xslt] with mock endpoint [mock:xslt:trans.xslt]
INFO o.a.c.i.InterceptSendToMockEndpointStrategy - Adviced endpoint [file://test/data/audit/?fileName=%24%7Bheader.outFileName%7D] with mock endpoint [mock:file:test/data/audit/]
INFO o.a.camel.spring.SpringCamelContext - StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
TradePublisherRoute.java
#Override
public void configure() throws Exception {
logger.info("TradePublisherRoute.configure() : trade-publisher started configuring camel route.");
from("{{trade-publisher.sourceEndpoint}}")
.doTry()
.bean(tradeService)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
String dateStr = Constant.dateFormatForFileName.format(new Date());
logger.info("this is getting executed : " + dateStr);
exchange.setProperty(Constant.KEY_INCOMING_XML_FILE_NAME, "REQ-" + dateStr + Constant.AUDIT_FILE_EXTENSION);
exchange.setProperty(Constant.KEY_OUTGOING_XML_FILE_NAME, "RESP-" + dateStr + Constant.AUDIT_FILE_EXTENSION);
}
})
.marshal()
.jacksonxml(true)
.wireTap("{{trade-publisher.requestAuditDir}}" + "${header.inFileName}")
.to("{{trade-publisher.xsltFile}}")
.to("{{trade-publisher.outboundQueue}}")
.to("{{trade-publisher.responseAuditDir}}" + "${header.outFileName}")
.bean(txnService, "markSuccess")
.endDoTry()
.doCatch(Exception.class)
.bean(txnService, "markFailure")
.log(LoggingLevel.ERROR, "EXCEPTION: ${exception.stacktrace}")
.end();
TradePublisherRouteTest.java
#ActiveProfiles("test")
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest(classes = TradePublisherApplication.class)
#MockEndpoints
public class TradePublisherRouteTest {
#EndpointInject(uri = "{{trade-publisher.outboundQueue}}")
private MockEndpoint mockQueue;
#EndpointInject(uri = "{{trade-publisher.sourceEndpoint}}")
private ProducerTemplate producerTemplate;
#MockBean
TradeService tradeService;
private List<Transaction> transactions = new ArrayList<>();
#BeforeClass
public static void beforeClass() {
}
#Before
public void before() throws Exception {
Transaction txn = new Transaction("TEST001", "C001", "100", "JPM", new BigDecimal(100.50), new Date(), new Date(), 1000, "P");
transactions.add(txn);
}
#Test
public void testRouteConfiguration() throws Exception {
Mockito.when(tradeService.searchTransaction()).thenReturn(new Data(transactions));
producerTemplate.sendBody(transactions);
mockQueue.expectedMessageCount(1);
mockQueue.assertIsSatisfied(2000);
}
Please correct me if i am doing something wrong!

Camel SQL - Put DataSource to SimpleRegistry in Spring Boot

I am using Spring Boot to initiate a camel route that uses Camel-sql to query MySQL DB and call a REST service.
application.properties
db.driver=com.mysql.jdbc.Driver
db.url=mysql://IP:PORT/abc
db.username=abc
db.password=pwd
Application.java
public static void main(String[] args) {
SpringApplication.run(WlEventNotificationBatchApplication.class, args);
}
DataSourceConfig.java
public class DataSourceConfig {
#Value("${db.driver}")
public String dbDriver;
#Value("${db.url}")
public String dbUrl;
#Value("${db.username}")
public String dbUserName;
#Value("${db.password}")
public String dbPassword;
#Bean("dataSource")
public DataSource getConfig() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(dbDriver);
dataSource.setUrl(dbUrl);
dataSource.setUsername(dbUserName);
dataSource.setPassword(dbPassword);
return dataSource;
}
}
WLRouteBuilder.java
#Component
public class WLRouteBuilder extends RouteBuilder {
#Autowired
private NotificationConfig notificationConfig;
#Autowired
private DataSource dataSource;
#Override
public void configure() throws Exception {
from("direct:eventNotification")
.to("sql:"+notificationConfig.getSqlQuery()+"?dataSource="+dataSource)
.process(new RowMapper())
.log("${body}");
}
}
I see the below error when I run, found out that Camel is unable to find DataSource bean in registry. I am quite not sure how to inject "DataSource" to Registry in Spring Boot using Java DSL.
?dataSource=org.springframework.jdbc.datasource.DriverManagerDataSource%40765367 due to: No bean could be found in the registry for: org.springframework.jdbc.datasource.DriverManagerDataSource#765367 of type: javax.sql.DataSource
Its the name of the bean that Camel uses in the uri, where you refer to it using the # syntax as documented here: http://camel.apache.org/how-do-i-configure-endpoints.html (referring beans)
So something alike
.to("sql:"+notificationConfig.getSqlQuery()+"?dataSource=#dataSource"
Where dataSource is the name of the bean that creates the DataSource, which you can give another name eg
#Bean("myDataSource")
And then the Camel SQL endpoint is
.to("sql:"+notificationConfig.getSqlQuery()+"?dataSource=#myDataSource"

Resources