Camel: pollEnrich and access to the Exchange - java

I have this route
from(URI_WEBSERVICE)
.convertBodyTo(Entrada.class)
.process(new ProcessorTratarWS())
.pollEnrich("ftp://10.100.8.2/entradaCamel?username=USER&password=PASSWORD&delete=true&fileName=${property.archivoRespuesta}", timeOut, new EstrategiaConfirmacion())
.to(WS_RESPONDER)
In ProcessorTratarWS() I set the value of property.archivoRespuesta and is the name of the file that the pollEnrich should donwload.
But, documentation says that "PollEnrich does not have access to the Exchange". It means the PollEnrich can't read the value of ${property.archivoRespuesta}
Are there some alternative ways to do in Camel the same thing I'm trying?
Thanks!

From http://camel.apache.org/content-enricher.html
...
Instead of using enrich you can use Recipient List and have dynamic
endpoints and define an AggregationStrategy on the Recipient List
which then would work as a enrich would do. ...
try something like:
from(URI_WEBSERVICE)
.convertBodyTo(Entrada.class)
.process(new ProcessorTratarWS())
.recipientList(simple("ftp://10.100.8.2/entradaCamel?username=USER&password=PASSWORD&delete=true&fileName=${property.archivoRespuesta}")).aggregationStrategy(new EstrategiaConfirmacion())
.to(WS_RESPONDER)
Edit:
The above code is to save file in FTP server.
If you want to poll file from the FTP server you can try
from(URI_WEBSERVICE)
.convertBodyTo(Entrada.class)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
// logic of ProcessorTratarWS goes here
ConsumerTemplate consumer=exchange.getContext().createConsumerTemplate();
String filename=exchange.getProperty("archivoRespuesta",String.class);
Object file=consumer.receiveBody("ftp://10.100.8.2/entradaCamel?username=USER&password=PASSWORD&delete=true&fileName="+filename,timeOut);
// logic of EstrategiaConfirmacion goes here
}
})
.to(WS_RESPONDER);
Disclaimer: I have not used polling consumer much and there could be more elegant/efficient solution

You can use "simple" expression
also use "exchangeProperty" instead of "property" in the string
from(URI_WEBSERVICE)
.convertBodyTo(Entrada.class)
.process(new ProcessorTratarWS())
.pollEnrich().simple("ftp://10.100.8.2/entradaCamel?username=USER&password=PASSWORD&delete=true&fileName=${exchangeProperty.archivoRespuesta}")
.timeout(timeOut)
.aggregationStrategy(new EstrategiaConfirmacion())
.to(WS_RESPONDER)

Related

How to create an IAM User using apache camel?

I have a camel rest api. I was trying to create an IAM User using apache camel framework. The code is like so ->
.post("iam-create-user")
.route()
.process(new Processor(){
#Override
public void process(Exchange exchange) throws Exception {
exchange.getIn().setHeader("CamelAwsIAMUsername", "new-user");
})
.to("aws2-iam://current-user?accessKey=insert&secretKey=insert&operation=createUser")
.endRest();
I am getting this error java.lang.NullPointerException: null. What is the correct way of doing this? It shows in the camel docs to use a URL like so to("aws2-iam://test?iamClient=#amazonIAMClient&operation=createUser"). What do we put as iamClient?
The iamClient reported in the example (#amazonIAMClient) is an instance of IAMClient you'll need to put in the Camel registry with a bind name amazonIAMClient.
I think you need to specify region as aws-global for this particular component.
Maybe add the full stack trace of your error.

Restrict Camel route to HTTP method

I have a Camel route definition like this one:
#Component
public class AggregateRouter extends AbstractRouteBuilder {
#Override
public void configure() throws Exception {
super.configure();
from("{{endpoint.users}}/{id}?matchOnUriPrefix=true")
.to("bean:routeUtils?method=validateQueryParams"))
.to("bean:routeUtils?method=loadRouteProperties"))
.to("{{uri.api.users}}")
.unmarshal().json(JsonLibrary.Jackson, Map.class)
.to("bean:routeUtils?method=extractAndAddToProperty"))
.to("bean:routeUtils?method=prepareAggregateRestCalls"))
.multicast()
.stopOnException()
.to("seda:operation1")
.to("seda:operation2")
.end()
.setBody(simple("${property.result}"))
.marshal().json(JsonLibrary.Jackson)
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"));
from("seda:operation2")
.toD("{{uri.api.users.operation2}}")
.unmarshal()
.json(JsonLibrary.Jackson, List.class)
.to("bean:userService?method=addOp2"));
from("seda:operation1")
.toD("{{uri.api.users.operation1}}")
.choice()
.when(AbstractHelper::isOk)
.unmarshal()
.json(JsonLibrary.Jackson, List.class)
.to("bean:userService?method=addOp1"))
.otherwise()
.unmarshal()
.json(JsonLibrary.Jackson, Map.class)
.to("bean:userService?method=handleRouteSubscriptionException"))
.end();
}
}
I want to be able to use this definition only when the HTTP request comes into the integration layer as a GET request. The issue now is: I have two more operations (PUT and DELETE), but I don't want a "special" processing for those two (at least for now)...and they are behaving as GET since this route definition is "intercepting" and handling the request(s).
I can't use a Rest DSL (the project is currently like). I also tried using the &httpMethodRestrict like {{endpoint.users}}/{id}?matchOnUriPrefix=true&httpMethodRestrict=PUT but it's not working also.
Any clues?
i also think that httpMethodRestrict is the way to go. The docs are quite vague about the parameter... Try to use it like httpMethodRestrict=GET (read: restrict the requests to GET)
Another possible solution might be using the header information Exchange.HTTP_METHOD like .filter(header("Exchange.HTTP_METHOD").isEqualTo("GET")) - just to get the idea (i didn't try it out)

Invoke route from Processor

I'm using Camel to integrate 2 systems. I have defined different routes and one of the routes consumes from a specific rabbitmq queue and send it to a REST service. Nothing fancy here, the route looks like this:
public class WebSurfingRabbitToRestRoute extends RouteBuilder{
#Override
public void configure() throws Exception {
from("rabbitmq://rabbit_host:port/Rabbit_Exchange").
setHeader("CamelHttpMethod", constant("POST")).
setHeader("Content-Type", constant("application/json")).
bean(TransformResponse.class, "transform").
to("http4://rest_service_host:port/MyRestService).
}
}
As you can see, i process every message before sending it to the rest service since i need to adjust some things. The problem comes when i find out that sometimes (i dont know how or when), the system that publish into rabbit, send 2 messages concatenated at once.
What i expect to get is a simple json like this:
[{field1:value1, field2:value2}]
What i sometimes get is:
[{field1:value1, field2:value2},{field1:value3, field2:value4}]
So when i face this scenario, the rest service im routing the message to, fails (obviously).
In order to solve this, i would like to know if there is a way to invoke a route from inside a processor. From the previous snippet of code you can see that Im calling the transform method, so the idea will be to do something like the following pseudo-code, because after the route is already fired, i cant split the events and send them both within the same route "instance", so i thought about invoking a different route that i can call from here which will send the message2 to the very same rest service.
public class TransformRabbitmqResponse {
public String transform(String body) throws Exception {
// In here i do stuff with the message
// Check if i got 2 messages concatenated
// if body.contains("},{") {
// split_messages
// InvokeDifferentRoute(message2)
//}
}
}
Do you guys think this is possible?
One option (though I am not sure this is the best option) would be to split this up into two different routes using a direct endpoint.
public class WebSurfingRabbitToRestRoute extends RouteBuilder{
#Override
public void configure() throws Exception {
from("rabbitmq://rabbit_host:port/Rabbit_Exchange")
.setHeader("CamelHttpMethod", constant("POST"))
.setHeader("Content-Type", constant("application/json"))
.bean(TransformResponse.class, "transform");
from("direct:transformedResponses")
.to("http4://rest_service_host:port/MyRestService");
}
}
And then in your transform bean, you can use camel Producer Template to publish the transformed payload(s) to your new direct endpoint (assuming you are using json?).
producerTemplate.sendBody("direct:transformedResponses", jsonString);

Camel RX is not deleting objects consumed from S3

If I configure Camel using a RouteBuilder:
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("aws-s3://my-bucket?accessKey=******&secretKey=******")
.to("stream:out");
}
});
Then each object in the my-bucket is consumed and then immediately deleted.
However if I use Camel RX:
new ReactiveCamel(context).toObservable("aws-s3://my-bucket?accessKey=*****&secretKey=*****", String.class)
.subscribe(System.out::println);
I find that objects are never deleted, the same objects are continually consumed.
The S3Consumer appears to have an 'OnCompletion' callback which will remove an object after it has been consumed. The callback is only called when using the RouteBuilder approach.
I expected both approaches to work similarly but it's clear I am missing something. Is it possible to make the 'reactive approach' delete the objects once they are consumed?
EDIT: I am using Camel 2.15.2.
Ah yeah thanks for spotting this. This is a bug in camel-rx and I have logged a ticket to get this fixed: https://issues.apache.org/jira/browse/CAMEL-8747
The UoW is not enabled when using camel-rx.

Apache Camel timeout during finding data through a CustomProcessor

I seem to get timeout errors after 20 seconds. I have a custom processor the implements Processor. I inject a DAO and when finding the data within the custom processor it takes longer to find the data on the Apache Camel side and it timeouts. If I run the same code without Apache Camel it runs instantly. By doing a SELECT inside the CustomProcessor it takes longer to find the data.
The memory reference for the DAO are the same, so in the test the data is fetched immediately and the CustomProcessor hangs for 20 seconds before the data is receieved and it throws an Exception.
I am unable to figure out what is the cause of the problem.
I have located the code on Githib: https://github.com/rajivj2/example2
The problem is on line 27 of StatusHibernateDAO. I use a in memory database with only one table. There is only data populated.
When using the CustomProcessor without Apache Camel it works perfectly.
I hope you can help.
Since you are using Annotation Based configuration, the embedded Active MQ broker might be stopped unexpectedly, it’s good to use external broker (ex: tcp://host: 61616).
It seems QoS settings on the JMS endpoint is not working. You can the Header values from ProducerTemplate.
Since you are using producerTemplate.requestBody the activemq end point assumes that it is Request and Replay exchange(InOut) and since there is no response from route timeout occurring. If you want to implement (InOut) follow the instructions at http://camel.apache.org/jms.html. Since your Routebuilder is InOnly, you need to send the disableReplyTo header from the ProducerTemplate.
Replace your test method testMessageSendToConsumerQueueRemoteId as
#Transactional
#Test
public void testMessageSendToConsumerQueueRemoteId() throws Exception {
Status status = new Status();
status.setUserId(10);
statusDAO.save(status);
Endpoint mockEndpoint = this.context.getEndpoint(properties.getProperty("activemq.destination"));
PollingConsumer consumer = mockEndpoint.createPollingConsumer();
producerTemplate.sendBodyAndHeader(source, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><example><remoteid>10</remoteid></example>","disableReplyTo","true");
Status savedStatus = consumer.receive(100).getIn().getBody(Status.class);
logger.info("savedStatus "+savedStatus.getID()+" "+savedStatus.getUserId());
assertNotNull(savedStatus);
}
Replace your ContentEnricherProcessor’s process method as
public void process(Exchange exchange) throws Exception {
Message message = exchange.getIn();
Entity entity = (Entity) message.getBody();
Status status = process(entity);
message.setBody(status);
exchange.getOut().setBody(status);
}
And your camel.property file should be
source=activemq:queue:deliverynotification
activemq.location=tcp://localhost:61616
activemq.destination=activemq:queue:responsenotification
If you want to receive back the generated response, you need to change your RouteBuilder.
from(properties.getProperty("source"))
.process(new ResponseProcessor())
.inOnly("direct:z")
.end();
from("direct:z")
.unmarshal()
.jaxb("com.example.entities.xml").convertBodyTo(Entity.class)
.multicast()
.to("direct:x")
.end();
from("direct:x").transacted()
.process((ContentEnricherProcessor) applicationContext.getBean("contentEnricherProcessor"))
.to(properties.getProperty("activemq.destination"));
Then change the producer patterns like
Status response = producerTemplate.requestBody(source, "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><example><remoteid>11</remoteid></example>",Status.class);
Here is the ResponseProcessor process method
public void process(Exchange exchange) throws Exception {
Message outMsg = exchange.getIn().copy();
exchange.setOut(outMsg);
}
Camel ROCKS... you can implement almost any use case or pattern of Enterprise Integration :)

Categories