Overriding HTTP client config while declaring routes - apache-camel

I have a web application from which I'm calling around 50-60 rest/soap apis. For this, I've created routes in JAVA DSL. Now, to have default application level timeout settings, I've done configuration like this-
public class DefaultHttpClientConfig implements HttpClientConfigurer { // http4
#Override
public void configureHttpClient(HttpClientBuilder clientBuilder) {
clientBuilder.setDefaultRequestConfig(
RequestConfig.custom()
.setConnectTimeout(1000)
.setSocketTimeout(1000).build());
}
}
and I've set this in camel context like this-
static CamelContext ctx = new DefaultCamelContext();
static {
try {
HttpComponent httpComponent = ctx.getComponent("http4", HttpComponent.class);
httpComponent.setConnectionTimeToLive(10);
httpComponent.setHttpClientConfigurer(new DefaultHttpClientConfig());
ctx.addRoutes(new DirectRestRouteBuilder());
ctx.start();
} catch (Exception e) {
e.printStackTrace();
}
}
Now when creating individual routes, I want to override these configuration, so I'm trying this as shown below-
from("direct:success")
.to("http4://localhost:8089/mockcarrier/success?httpClient.socketTimeout=8000");
However, it seems that the direct configuration in JAVA DSL is not picked up. Where am I going wrong?

Add DefaultHttpClientConfig to the Camel registry and set it on the route with the httpClientConfigurer parameter (Camel documentation).
Like this:
.to("http4://localhost:8089/mockcarrier/success?httpClientConfigurer=yourCustomConfigurerNameInTheRegistry");

Related

Apache Camel FTP integration

I have just started working around the Apache Camel. I have a requirement to implement an FTP/FTPS/SFTP client, which would be used to fetch the files from the respective servers. I was looking into the possibility of using Apache Camel to do this but I am still confused after going through the examples and the tutorials.
The requirement is to fetch the files from the FTP/SFTP servers when the request is received from the scheduler.
Following is the route created using EndPoint-DSL
#Component
public class FtpReceiveRoute extends EndpointRouteBuilder {
#Override
public void configure() throws Exception {
from(
ftp("localhost:2001/home/admin")
.account("admin")
.password("admin12345")
.recursive(true)
)
.routeId("ftpReceive")
.log("From done!")
.to("log:ftp-log")
.log("To done!!");
}
}
I am trying to use the above route by invoking it when the request is made to fetch the file like below.
#Override
protected FtpResponse doMessage(String param, FtpRequest req) {
FtpResponse response = new FtpResponse ();
CamelContext ctx = new DefaultCamelContext();
ctx.addRoutes(##route); //FtpReceiveRoute, add the Routebuilder instance as EndpointRouteBuilder is acceptable.
ctx.start();
//Might need to induce sleep so that all the files are downloaded
ctx.stop();
return response;
}
The confusion is around how to invoke the Camel process with the route. I have used EndpointRouteBuilder to create the route because of the type-safe creation of the endpoint URI. I am not getting an option to add this route to the CamelContext as it expects the RouteBuilder instance which is not type-safe.
Further, the CamelContext is the engine and to invoke the route I would need to start and stop this engine. This I am not able to digest if I need to start and stop the engine to execute a route then I would need to induce some sleep in between so that all files are downloaded. Just to add there are more routes that I need to add with the implementation. Once the engine is started it would load and execute all the added routes which is not the requirement.
Maybe I am not getting how to use this properly. Any resources aiding my situation are welcome. Thanks.
You should not create and start new camel context every time you want to fetch file from server. What you should do instead is start one when your application starts and use that for all your exchanges.
You can use Spring-boot to initialize CamelContext and add annotated RouteBuilders to it automatically. Check the maven archetype camel-archetype-spring-boot for example.
If you want to call camel routes from Java you can Inject CamelContext to your bean and use it to create ProducerTemplate. This can be used to invoke Routes defined in the RouteBuilder.
Using ProducerTemplate.send you can get the resulting exchange.
Using producer template
Using File-component which works very similary to ftp-component.
package com.example;
import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
import org.springframework.stereotype.Component;
#Component
public class MySpringBootRouter extends EndpointRouteBuilder {
#Override
public void configure() {
from(direct("fileFromFTP"))
.routeId("fileFromFTP")
// reads files from <project>/input using file consumer endpoint
.pollEnrich(file("input"), 1000)
// If file is found, convert body to string.
// Which in this case will read contents of the file to string.
.filter(body().isNotNull())
.convertBodyTo(String.class)
.end()
;
}
}
package com.example;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.support.DefaultExchange;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import static org.apache.camel.builder.endpoint.StaticEndpointBuilders.direct;
#Configuration
#EnableScheduling
public class MySpringBean {
#Autowired
CamelContext camelContext;
#Scheduled(fixedRate = 1000)
public void scheduledTask() {
System.out.println("Scheduled Task!");
if(camelContext.isStopped()) {
System.out.println("Camel context not ready yet!");
return;
}
useProducerTemplate();
}
public void useProducerTemplate(){
ProducerTemplate producerTemplate = camelContext.createProducerTemplate();
Exchange inExchange = new DefaultExchange(camelContext);
//synchronous call!
Exchange result = producerTemplate.send(direct("fileFromFTP").toString(), inExchange);
String resultBody = result.getMessage().getBody(String.class);
String fileName = result.getMessage().getHeader(Exchange.FILE_NAME, String.class);
if(resultBody != null){
System.out.println("Consumed file: "+ fileName + " contents: " + resultBody.toString());
}
else{
System.out.println("No file to consume!");
}
}
}
Depending on what you need to do with the files you could probably do that inside camel route. Then you would only need to call the producerTemplate.sendBody.
public void useProducerTemplate(){
ProducerTemplate producerTemplate = camelContext.createProducerTemplate();
Exchange inExchange = new DefaultExchange(camelContext);
producerTemplate.sendBody(direct("fileFromFTP").toString(), inExchange);
}
Starting stopping camel route
If you want to start polling file consumer only for a short while you can do start the route and use for example aggregation timeout to shutdown the route when no new files have been received in any given duration.
#Component
public class MySpringBootRouter extends EndpointRouteBuilder {
#Override
public void configure() {
AggregationStrategy aggregateFileNamesStrategy = AggregationStrategies
.flexible(String.class)
.accumulateInCollection(ArrayList.class)
.pick(header(Exchange.FILE_NAME))
;
from(file("input"))
.routeId("moveFilesRoute")
.autoStartup(false)
.to(file("output"))
.to(seda("moveFilesRouteTimeout"));
;
from(seda("moveFilesRouteTimeout"))
.routeId("moveFilesRouteTimeout")
.aggregate(constant(true), aggregateFileNamesStrategy)
.completionTimeout(3000)
.log("Consumed files: ${body.toString()}")
.process(exchange -> {
exchange.getContext().getRouteController().stopRoute("moveFilesRoute");
})
.end()
;
}
}
public void startMoveFilesRoute() {
try {
System.out.println("Starting moveFilesRoute!");
camelContext.getRouteController().startRoute("moveFilesRoute");
//Sending null body moveFilesRouteTimeout to trigger timeout if there are no files to transfer
camelContext.createProducerTemplate().sendBody(seda("moveFilesRouteTimeout").toString(), null);
} catch(Exception e) {
System.out.println("failed to stop route. " + e);
}
}

Explicitly define Camel Routes in Quarkus

Current behaviour: When I'm running a Quarkus App with Camel it automatically starts all the RouteBuilder Extensions as Routes.
What I want to achieve: On startup only the Routes that I configured are started.
What I tried:
With the following snippet it's possible to explicitly start the CamelMainApplication but I dont know how to get control over e.g. the CamelContext at this point where I would be able to configure my routes.
#QuarkusMain
public class Main {
public static void main(String[] args) throws Exception {
Quarkus.run(CamelMainApplication.class, args);
}
}
On the Route I can use .noAutoStartup() to disable the route on startup. But this means that it's not the default for all routes to be disabled at first and second I don't know where to activate them as I don't know where in a Quarkus App I can get a hand on the Camel Context to activate the route.
With the following in my application.yml I can disable the automatic route discovery but then the remaining question is how I can manually start the route, e.g. in my QuarkusMain class.
quarkus:
camel:
routes-discovery:
enabled: false
I think it is best way. Quarkus has properties for include and exclude route as pattern. this properties is List You can add one to N
quarkus.camel.routes-discovery.exclude-patterns=tes.Comp,tes.package.MyRoute
quarkus.camel.routes-discovery.include-patterns=test.mypackage.XX
I had this same problem I ended up doing something like the following:
#QuarkusMain
public class Main implements QuarkusApplication {
#Inject
OrderService orderService;
#Override
public int run(String... args) throws Exception {
CamelRuntime runtime = Arc.container().instance(CamelRuntime.class).get();
runtime.start(new String[]{});
orderService.handleOrders(args[0]); //this would inject the camelContext and start the route.
return 0;
}

Using Camel Endpoint DSL with #EndpointInject creating different endpoints

What is the proper way to use endpoint DSL and then reference the endpoint with ProducerTemplate? When creating a route and using endpoint DSL, it seems that Camel is creating a different uri for the endpoint. My EndpointRouteBuilder class:
#Component
public class MyRoutes extends EndpointRouteBuilder {
#Override
public void configure() throws Exception {
from(seda("STATUS_ENDPOINT"))
.routeId("stateChangeRoute")
.to(activemq("topic:statusTopic"))
}
}
and then injecting the endpoint to ProducerTemplate
#Component
public class StateChangePublisher {
#EndpointInject(value="seda:STATUS_ENDPOINT")
private ProducerTemplate producer;
public void publish(String str) {
try {
producer.sendBody(str);
} catch(CamelExecutionException e) {
e.printStackTrace();
}
}
}
When camel starts, I see two entries in the log:
o.a.camel.component.seda.SedaEndpoint : Endpoint seda:STATUS_ENDPOINT is using shared queue: seda:STATUS_ENDPOINT with size: 1000
o.a.camel.component.seda.SedaEndpoint : Endpoint seda://STATUS_ENDPOINT is using shared queue: seda://STATUS_ENDPOINT with size: 1000
The queue eventually fills up and nothing gets delivered to the "to" endpoint.
If I define the route without using the endpoint DSL method "seda()"
from("seda:STATUS_ENDPOINT")
then it works.
Is this a bug or am I doing something wrong?
I'm using camel 3.2.0 and
This was a bug in the endpoint dsl. Try upgrading to camel 3.3.0. I think it was fixed in the new release.
https://issues.apache.org/jira/browse/CAMEL-14859

Apache Camel example to get data from HTTP source does not get

To retrieve some open data from a remote web server to process, I'm trying out Apache Camel.
The problem is that it seems that the data is never received. I have tried the jetty, ahc and cxf components but can't get it to work. For example like this:
import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
public class CamelHttpDemo {
public static void main(final String... args) {
final CamelContext context = new DefaultCamelContext();
try {
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
this.from("direct:start")
.to("ahc:http://camel.apache.org/")
.process(exchange -> {
System.out.println(exchange);
});
}
});
context.start();
Thread.sleep(10000);
context.stop();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
No output is written so the line System.out.println(exchange); is never executed and I assume the data is not retrieved.
I'm using the most recent version of Apache Camel, 2.17.1.
You need some message producer in your route to emit Exchange that would trigger the http component. Your route starts with direct:start which cannot emit new Exchanges, it just sits and waits for someone to initiate the process.
The easiest way to make your route work is to replace direct:start with some producer. For instance, replacing it with this timer .from("timer://foo?fixedRate=true&period=10000") will trigger your http-request once every 10 seconds.
If you want to initiate the request manually, you need to create a ProducerTemplate and use it to send a message to direct:start. That would be:
ProducerTemplate template = context.createProducerTemplate();
template.sendMessage("direct:start", "Message body");

Apache Camel routing to an interface (or rather adding listeners dynamically)

I am working on a simple use case that would allow clients to dynamically register for events from a JMS endpoint. My current implementation looks like this:
...
public void addListener(Event event, Listener listener){
try {
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from(event.from()).bean(listener);
}
});
} catch (Exception exception) {
exception.printStackTrace();
}
}
...
event.from() above would identify the endpoint from which the message would be consumed ("activemq:topic:market.stocks.update.ibm") and listener would be an implementation of the Listener interface.
I had envisaged a typical invocation as:
notifications.addListener(updateEvent, new Listener(){
void listen(){
System.out.println("Hey! Something got updated");
}
});
Except, of course, none of the above works since the camel route expects to have a concrete bean as the recipient and hence camel context fails to start-up.
What is the recommended way of adding bean end points dynamically?
answered on camel-users forum...
http://camel.465427.n5.nabble.com/Observer-Pattern-using-Camel-td4491726.html

Resources