Getting Column headers into CSV while using camel Routing - apache-camel

I have a requirement where in i have to get data from the table including column names and save it to CSV. I have coded like the below to get only the data from table not column headers. Can someone help me with this
public void configure() throws Exception {
from("restlet:http://localhost:8583/.....")
.setBody(simple("select * from userDetails"))
.to("jdbc:dataSource")
.marshal()
.csv()
.to("file://C:/test.csv");
}
Appreciate your help...

The column names would be available to you in the exchange after the to("jdbc:dataSource") statement in the header-CamelJdbcColumnNames.
So, basically the solution would be close to this:
public void configure() throws Exception {
from("restlet:http://localhost:8583/.....")
.setBody(simple("select * from userDetails"))
.to("jdbc:dataSource")
.to("direct:setColumnNames")
.split(body())
.marshal().csv()
.to("file://C:/test.csv");
from("direct:setColumnNames")
.getHeader("CamelJdbcColumnNames")
.marshal().csv()
.to("file://C:/test.csv");
}
I'd suggest you write a custom processor for yourself that would solve your cause as well.

Related

Can we provide a processor before onFallbackViaNetwork() in camel-hystrix-eip

I'm using camel-hystrix-eip in my project, I want a processor before my fallback, but it's not working
from("direct:sample").id("id:direct:sample").process(requestProcessor)
.hystrix()
.to(endPoint)
.onFallbackViaNetwork()
.to(fallback_endPoint)
I want to alter my fallback_endpoint using a processor, but seems like after onFallbackViaNetwork() we have to immediately provide to().
please suggest if there any way to do so.
I tried something like below, but it's not working.
from("direct:sample").id("id:direct:sample").process(requestProcessor)
.hystrix()
.to(endPoint)
.onFallbackViaNetwork()
.process(fallbackProcessor)
.to(fallback_endPoint)
Actually, I'm using requestProcessor to override the actual endpoint, and in case of a fallback, fallback_endPoint is also getting overridden, is there any way to avoid this.
You can have a processor after onFallbackViaNetwork(). You can also use the toD EIP to send the message to a dynamic endpoint.
Based on your code, you could set a Header MyEndpoint which contains your new endpoint string, and then reference it using .toD("${header.MyEndpoint}"). Repeat this pattern whenever you need to set a dynamic endpoint.
For example:
from("direct:sample")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
// do something
exchange.getIn().setHeader("EndpointHystrix", "mock:hystrix");
}
})
.hystrix()
.toD("${header.EndpointHystrix}")
.onFallbackViaNetwork()
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
// do something more
exchange.getIn().setHeader("EndpointFallback", "mock:fallback");
}
})
.toD("${header.EndpointFallback}")
.end()
.to("...");
I've tested this in Camel 2.20.0 and 2.21.0.

Load Camel's body dynamically depending on header

Hi I want to set to my body an SQL statement in order to be used by the jdbc component afterwards. I have my sql scripts in the resources, thus i can take advantage of simple language to load sth from resources with the usage of resource:classpath. The problem is that url is not static and I want to load different files depending on a header that I've got (``).
<!-- INSERT data -->
<setBody>
<simple>resource:classpath:sql/${header.CamelCustomer}/Insert.sql</simple>
</setBody>
Assuming that I have 2 customers A, and B. That mean that I also have 2 directories
resources/sql/A/Insert.sql
resources/sql/b/Insert.sql
How it is possible to load the different files with the usage of the header?
Thanks in advance!
You can use Content Enricher integration pattern and wrap your simple expression with Language component to get it evaluated.
This should return expected results:
Java DSL:
.enrich().simple("language:simple:resource:classpath:sql/${header.CamelCustomer}/Insert.sql")
XML DSL:
<enrich><simple>language:simple:resource:classpath:sql/${header.CamelCustomer}/Insert.sql</simple></enrich>
I have created sample unit test for demonstration.
src/test/resources/sql/A/Insert.sql
INSERT something INTO A;
src/test/resources/sql/B/Insert.sql
INSERT something INTO B;
DynamicLoadResourceTest
public class DynamicLoadResourceTest extends CamelTestSupport {
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.enrich().simple("language:simple:resource:classpath:sql/${header.CamelCustomer}/Insert.sql")
.to("mock:done");
}
};
}
#Test
public void testContentEnrichResource() throws Exception {
MockEndpoint done = getMockEndpoint("mock:done");
Map<String, Object> headers = new HashMap<>();
headers.put("CamelCustomer", "A");
sendBody("direct:start", null, headers);
headers.put("CamelCustomer", "B");
sendBody("direct:start", null, headers);
done.setExpectedCount(2);
done.assertIsSatisfied();
Assert.assertEquals(
"INSERT something INTO A;",
done.getExchanges().get(0).getIn().getBody()
);
Assert.assertEquals(
"INSERT something INTO B;",
done.getExchanges().get(1).getIn().getBody()
);
}
}

How to use Apache Camel exchange messages?

I am newcomer in Apache Camel. Please have a look to my code bellow:
I have a service which exposed as cxf webservice:
interface CxfService{
public OutputType hello(InputType input);
}
This is my route:
from("cxf:/test?serviceClass=" + CxfService.class.getName())
.to("log:cxfLog1")
.recipientList(simple("direct:${header.operationName}"));
from("direct:hello")
.process(new Processor(){
public void process(Exchange exchange) throws Exception {
InputType file = exchange.getIn().getBody(InputType.class);
exchange.getOut().setBody(new OutputType());
}
});
The code works as expected, it consume InputType and produce OutputType.
I want to borrow my body to do another stuffs, so i rewrite that like this:
from("cxf:/test?serviceClass=" + CxfService.class.getName())
.to("log:cxfLog1")
.recipientList(simple("direct:${header.operationName}"));
from("direct:hello")
.process(new Processor(){
public void process(Exchange exchange) throws Exception {
InputType file = exchange.getIn().getBody(InputType.class);
exchange.getOut().setHeader("header.temporary", new OutputType());
}
})
.to("some endpoint")
.setBody(simple("${header.temporary}"));
This webservice consume InputType and produce nothing. What wrong with that?
In your second piece of code, when setting the header.temporary, you should change two things:
setHeader("temporary", new OutputType()) - the 'header' prefix isn't
needed - you're addressing headers directly via the method call.
Use getIn() instead of getOut(). The input will get copied to the
output. You may want to do some research into the procedure for
Camel building the out message for details - I'm not 100% sure of
this one.
Change
exchange.getOut().setHeader("header.temporary", new OutputType());
To
exchange.getIn().setHeader("temporary"), new OutputType());
.setHeader() is when you use the simple language. In 99% of the cases getIn() is sufficient.

Camel-cmis - metadata setting error

A simple test of setting the document class of document stored in filenet worked in camel-cmis 2.16.2. Below is the route
from("file://C:/Target/DMS/").process(new Processor() {
#Override
public void process(Exchange e) throws Exception {
e.getIn().getHeaders().put(PropertyIds.CONTENT_STREAM_MIME_TYPE, "application/pdf; charset=UTF-8");
e.getIn().getHeaders().put(CamelCMISConstants.CMIS_FOLDER_PATH, "/Test");
e.getIn().getHeaders().put("cmis:objectTypeId", "doc_Test");
e.getIn().getHeaders().put(PropertyIds.NAME, e.getIn().getHeader(Exchange.FILE_NAME));
}
}).to("cmis://http://test:9080/fncmis/resources/Service?repositoryId=TEST_REPO&username=TEST&password=RAW(TEST)");
When i check the document class of file stored in IBM Filenet - i could see the doc class as Test (symbolic name: doc_Test). But when i add atleast one of the parameter value of that class like below
e.getOut().getHeaders().put("prp_Field1","TestValue1");
Im getting NoSuchHeaderException for parameter "cmis:name" which i have already set as you can see the above route. Is this the correct way to set metadata parameters??
Below route for storing worked. Decide on the Customized classes,fields & data types, create metadata based on that & then same field names are to be set in camel headers before sending to cmis uri (Storing).
ObjectTypeId - Customized class name
CMIS_FOLDER_PATH - filenet folder path inside repository
NAME - File name to be stored
from("file://C:/Target/DMS/").process(new Processor() {
#Override
public void process(Exchange e) throws Exception {
e.getIn().getHeaders().put(CamelCMISConstants.CMIS_FOLDER_PATH, "/TEST");
e.getIn().getHeaders().put("cmis:objectTypeId", "doc_Test");
e.getIn().getHeaders().put(PropertyIds.NAME, fileName + ".pdf");
e.getOut().getHeaders().put("prp_Field1","TestValue1");
e.getOut().getHeaders().put("prp_Field2","TestValue2");
e.getOut().getHeaders().put("prp_Field3","TestValue3");
}
}).to("cmis://http://test:9080/fncmis/resources/Service?repositoryId=TEST_REPO&username=TEST&password=RAW(TEST)");
Hope it helps someone..

Camel route and MyBatis

I am relatively new to Camel and i've been struggling with a problem with a simple route that uses MyBatis to "feed" an ActiveMQ queue.
My route is as follows:
public class SearchItemProductionRouteRoute extends SpringRouteBuilder {
#Override
public void configure() throws Exception {
from("timer://pollTheDatabase?delay=5000")
.to("mybatis:selectSearchItem?statementType=SelectList&consumer.useIterator=true&consumer.onConsume=updateProcessingSearchItem")
.to("bean:searchItemProcesser?method=process")
.to("activemq:searchitemqueue");
}
}
The mybatis query is working fine. The query itself brings 4 records from the database. My need is that each row should become a message in the queue, but instead, i get 1 message with all the 4 rows in it.
The searchItemProcessor just prints de body of the message (thats how i know that the message contains all 4 records).
These are the queries that the above route uses:
<select id="selectSearchItem" resultMap="result" parameterType="java.util.HashMap">
SELECT * FROM SEARCH_REQUEST_ITEM SRI WHERE SRI.STATUS = '1'
</select>
<update id="updateProcessingSearchItem">
UPDATE SEARCH_REQUEST_ITEM SET STATUS = 2,
UPDATEDIN=SYSDATE, UPDATEDBY='XDRBATCH'
WHERE ID = #{ID}
</update>
If anyone can shed some light over this i'll be thankful!
EDIT:
Just found one workaround using the Splitter EIP. First i created this class:
public class XdrMessageSplitterBean {
#SuppressWarnings({ "rawtypes", "unchecked" })
public List<HashMap> splitBody(Object body) {
return (List<HashMap>) body;
}
}
Then add it to the route:
public void configure() throws Exception {
from("timer://pollTheDatabase?delay=5000")
.to("mybatis:selectSearchItem?statementType=SelectList&consumer.useIterator=true&consumer.onConsume=updateProcessingSearchItem")
.split().method("xdrMessageSplitterBean", "splitBody")
.to("bean:searchItemProcesser?method=process")
.to("activemq:searchitemqueue");
}
The bean must be declared in the camel-context.xml file:
<bean id="xdrMessageSplitterBean" name="xdrMessageSplitterBean"
class="package.of.bean.XdrMessageSplitterBean" />
It works, but does not feel right. If anyone has any suggestion it will be very welcomed.
You can consume from mybatis directly, and specify the polling frequency. You dont need the timer for that. There is a delay option you can use to set = 5000 for 5 seconds frequency.
public void configure() throws Exception {
from("mybatis:selectSearchItem?statementType=SelectList&consumer.useIterator=true&consumer.onConsume=updateProcessingSearchItem&delay=5000")
.split().method("xdrMessageSplitterBean", "splitBody")
.to("bean:searchItemProcesser?method=process")
.to("activemq:searchitemqueue");
}
I'd say you found the right way to do it. You should not consider it a workaround.
Actually, since mybatis returns a List I'm not even sure you need the "XdrMessageSplitterBean".

Resources