How to use a dynamic URI in From() - apache

As mentioned in Apache Camel, it allows to write dynamic URI in To(), does it allows to write dynamic URI in From().
Cause I need to call the multiple FTP locations to download the files on the basis of configuration which I am going to store it in database.
(FTPHost, FTPUser, FTPPassword, FTPSourceDir, FTPDestDir)
I will read these configuration from the DB and will pass it to the Camel route dynamically at runtime.
Example:
This is the camel route example that I have to write dynamically
<Route>
<from uri="ftp://${ftpUser}#${ftpHost}:${ftpPort}/${FTPSourceDir}?password=${ftpPassword}&delete=true"/>
<to uri="${ftpDestinationDir}"/>
</Route>
As you see in example, I need to pass these mentioned parameters dynamically.
So how to use dynamic uri in From()

You can read it from property file as follows,
<bean id="bridgePropertyPlaceholder" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
<property name="location" value="classpath:/config/Test.properties"/>
</bean>
<Route>
<from uri="ftp://{{ftpUser})#${{ftpHost}}:{{ftpPort}}/${{FTPSourceDir}}?password={{ftpPassword}}&delete=true"/>
<to uri="{{ftpDestinationDir}}"/>
</Route>
ftpUser, ftpHost.... - all are keys declared in Test.properties
If you want to get those variables from your exchange dynamically, you cannot do it in regular way as you mentioned in your example. You have to use consumer template as follows,
Exchange exchange = consumerTemplate.receive("ftp:"+url);
producerTemplate.send("direct:uploadFileFTP",exchange );
You have to do that from a spring bean or camel producer. Consumer template will consume from given component, and that producer template will invoke direct component declared in your camel-context.xml
Note: Consumer and Producer templates are bit costly. you can inject both in spring container and let the spring handle the life cycle.

From camel 2.16 on-wards, we can use pollenrich component to define polling consumer like file, ftp..etc with dynamic url/parameter value like below
<route>
<from uri="direct:start"/>
<pollEnrich>
<simple>file:inbox?fileName=${body.fileName}</simple>
</pollEnrich>
<to uri="direct:result"/>
</route>
Its awesomeeee!!!
Refer: http://camel.apache.org/content-enricher.html

I help a team who operates a message broker switching about a million message per day. There are over 50 destinations from which we have to poll files over all file sharing brands (FTP, SFTP, NFS/file: ...). Maintaining up to 50 deployments that each listen to a different local/remote directory is indeed an overhead compared with a single FILE connector capable of polling files at the 50 places according to the specific schedule and security settings of each... Same story for getting e-mail from pop3 and IMAP mailboxes.
In Camel, the outline of a solution is as follows:
you have no choice but use the java DSL to configure at least the from() part of your routes with an URI that you can indeed read/build from a database or get from an admin request to initiate a new route. The XML DSL only allows injecting properties that are resolved once when the Camel context is built and never again afterwards.
the basic idea is to start routes, let them run (listen or poll a precise resource), and then shutdown & rebuild them on demand using the Camel context APIs to manage the state of RouteDefinitions, Routes, and possibly Endpoints
personally, I like to implement such dynamic from() instantiation on minimalist routes with just the 'from' part of the route, i.e. from(uri).to("direct:inboundQueue").routeId("myRoute"), and then define - in java or XML - a common route chunk that handles the rest of the process: from("direct:inboundQueue").process(..).etc... .to(outUri)
I'll advise strongly to combine Camel with the Spring framework, and in particular Spring MVC (or Spring Integration HttpGateway) so that you will enjoy the ability to quickly build REST, SOAP, HTTP/JSP, or JMX bean interfaces to administer route creation, destruction, and updates within a Spring + Camel container, both nicely integrated.
You can then declare in the Spring application context a bean that extends SpringRouteBuilder, as usual when building Camel routes with the java DSL in Spring; in the compulsory #Override configure() method implementation, you shall save your routeDefinition object built by the from(uri) method, and assign it a known String route-id with the .routeId(route-id) method; you may for instance use the route-id as a key in a Map of your route definition objects already created and started, as well as a key in your DB of URI's.
then you extend the SpringRouteBuilder bean you have declared with new methods createRoute(route-id), updateRoute(route-id), and removeRoute(route-id); The associated route-id parameters needed for create or update will be fetched from the database or another registry, and the relevant method, running within the RouteBuilder bean, will take advantage from the getContext() facility to retrieve the current ModelCamelContext, which in turn is used to stopRoute(route-id), removeRoute(route-id), and then addRouteDefinition(here is where you need the routeDefinition object), and finally startRoute(route-id) (Note: beware of possible ghost Endpoints that would not be removed, as explained in the removeRoute() javadoc)
your administrative interface (which typically takes the form of a Spring #Controller component/bean that handles the HTTP/REST/SOAP traffic) will indeed have an easy job to get the previously created SpringRouteBuilder extension Bean injected by Spring in the controller bean, and thus access all the necessary createRoute(route-id), updateRoute(route-id), and removeRoute(route-id) methods that you have added to the SpringRouteBuilder extension Bean.
And that works nicely. The exact implementation with all the error handling and validation code that applies is a bit too much code to be posted here, but you have all the links to relevant "how to's" in the above.

I think you can implement your requirement within a Camel route.
Because you want to poll multiple FTP sites you'll have to somehow trigger this process. Maybe you could do this based on a Quartz2 timer. Once triggered you could read the configured FTP sites from your database.
In order to poll the given FTP sites you can use the Content Enricher pattern to poll (see: pollEnrich) a dynamically evaluated URI.
Your final basic route may look something like this (pseudocode):
from("quarz...")
to("sql...")
pollEnrich("ftp...")
...

Use Camel endpoint with spring spel expression.
Set up a Camel endpoint in the context so it can be accessed from any bean:
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<endpoint id="inventoryQueue" uri="#{config.jms.inventoryQueueFromUri}"/>
</camelContext>
Now you can reference the inventoryQueue endpoint within the `#Consume` annotation as follows:
#org.apache.camel.Consume(ref = "inventoryQueue")
public void updateInventory(Inventory inventory) {
// update
}
Or:
<route>
<from ref="inventoryQueue"/>
<to uri="jms:incomingOrders"/>
</route>

Related

Accessing spring cloud function rabbit binding details

I'm attempting to update the "Springwolf" library (used to document async API definitions) to support rabbit consumer listeners using spring cloud functions for spring cloud stream. In order to accomplish this I need to find from application context
spring cloud functions defined.
map this to a channel name
map that channel name to its binding.. hopefully pulling out exchange, queue and routing key values.
I've been able to successfully track down the defined functions, get the expected payload type of the function, and map that to a channel name using defined values in application.properties.
Depending on our application properties are set up I can get the other info from there but this is not always possible. Is there a way I can pull this data from defined spring beans.
I've found bean "bindingService" has a private field "consumerBindings" which has a field "destination" which has a field "bindings" and those bindings have all of the details that I need... but given they're private and buried I don't think this would be a good way to go.
I've also found spring.cloud.stream-org.springframework.cloud.stream.config.BindingServiceProperties has a "getBindings" method on it, but those values appear to be incomplete. for instance with a rabbit consumer defined as below the "group name" is generated randomly but its name is not available from the Bindings provided in BindingServiceProperties
spring.cloud.stream.bindings.anotherConsumerMethod-in-0.destination=someDestination2
spring.cloud.stream.bindings.anotherConsumerMethod-in-0.consumer.bindingRoutingKey=testgroup.testDestination.queue2
Any help/guidance would be awesome... also if the answer is "you can't do this" I completely understand.

How to change the operation name of a span with Apache Camel OpenTracing component?

I successfully added Apache Camel's OpenTracing component to my application. I can see traces in Jaeger UI. But the traces for the RabbitMQ component show only the exchange name without the routing key as operation name. Because of my application uses only one exchange with different routing keys, I need to see the routing key as operation name in my traces.
Research
With OpenTracing Spring RabbitMQ I could expose another customized RabbitMqSpanDecorator, see Span decorator:
Note: you can customize your spans by declaring an overridden RabbitMqSpanDecorator bean.
(However, I coulnd't change the operation name with the RabbitMqSpanDecorator at all, because the operation name is hard coded to producer or consumer.)
Unfortunately Apache Camel uses its own different implementation of a RabbitmqSpanDecorator to decorate spans. I wrote a custom class by overiding Apache Camel's RabbitmqSpanDecorator, but my custom class wasn't used.
Question
How can I change the operation name of a span with Apache Camel OpenTracing component for Apache Camel RabbitMQ component?
It is possible to change the Tracer implementation with ServiceLoader, see OpenTracing:
EXPLICIT
Include the camel-opentracing component in your POM, along with any specific dependencies associated with the chosen OpenTracing compliant Tracer.
To explicitly configure OpenTracing support, instantiate the OpenTracingTracer and initialize the camel context. You can optionally specify a Tracer, or alternatively it can be implicitly discovered using the Registry or ServiceLoader.
With the DefaultTracer it is also possible to change the RabbitmqSpanDecorator with ServiceLoader, see Tracer.java:
static {
ServiceLoader.load(SpanDecorator.class).forEach(d -> {
SpanDecorator existing = DECORATORS.get(d.getComponent());
// Add span decorator if no existing decorator for the component,
// or if derived from the existing decorator's class, allowing
// custom decorators to be added if they extend the standard
// decorators
if (existing == null || existing.getClass().isInstance(d)) {
DECORATORS.put(d.getComponent(), d);
}
});
}
Therefore, I had to add a file org.apache.camel.tracing.SpanDecorator containing the name of my custom RabbitmqSpanDecorator, see ServiceLoader:
Deploying service providers on the class path
A service provider that is packaged as a JAR file for the class path is identified by placing a provider-configuration file in the resource directory META-INF/services. The name of the provider-configuration file is the fully qualified binary name of the service. The provider-configuration file contains a list of fully qualified binary names of service providers, one per line.
My custom RabbitmqSpanDecorator:
public class CustomRabbitmqSpanDecorator extends RabbitmqSpanDecorator {
#Override
public String getOperationName(Exchange exchange, Endpoint endpoint) {
return ((RabbitMQEndpoint) endpoint).getRoutingKey();
}
}

Using Mule variables or properties accross transport barriers

Let say I get a inbound variable from an http connector when I use this URL
http://x.x.x.x:8080/post?post-message=Hallo wold.
How can I use the value of the #[header:INBOUND:post-message] accross the complete flow from after the HTTP connector all the way up tp the end. Should I use the Mule Object store to write it to ram?
This post shows the scope of variables but it seem there is not one thay can flow from start to en like a session bean
https://m-square.com.au/mule-school-the-mulemessage-property-scopes-and-variables/
Kind Regards.
If you need the variable available throughout the whole flow (and
other flows reached through a flow-ref) use the invocation scope
(set-variable to set, flowVars[] to read it)
If you need it to reach other flows through a transport (e.g. VM) put
it in the outbound or session scope.
If you need it to live as long as the app is running, through
different calls, use the Mule registry (volatile, only available as
long as the app is up) or the object store (which can be configured
as persistent, to hold state even if the app goes down).
Mule Session variables are good enough to get the value throughout application.
But if you need to use the value outside your application, then you can set mule outbound properties.
There are three type of scope level variables supported by mule .
You can use flow variable if you want the variable to be accessible throughout the whole flow.
You can use session variable if you want it to be accessible form other flows through a transport barrier.
Refer this blog for getting better understanding of how different types of variables are propagated between different mule flows.
http://blogs.mulesoft.com/dev/anypoint-platform-dev/mule-school-the-mulemessage-property-scopes-and-variables/

Use options set on apache camel direct endpoint

I am building a routing slip bean and want to apart from the dynamic routing also to send a parameter to the endpoints of the recipients list.
I wish to use something like "direct:test?param=value", where param and value are set inside the routing slip POJO
From what I understand from the direct component, a single "direct:test" endpoint will receive all the routed exchanges, however I need a way to read the param send from the routing slip
How can I use this param to the endpoint which receives the exchange? Is it a property of the exchange?
Pan,
I'd use the header functionality of camel. It's quite versatile and you can handle it using predicates for some nice DSL or spring control, or in a bean/processor by doing myExchange.getIn().getHeader("foo").

Apache CXF - validation of generated classes before sending to the API (in backing bean or where?)

I have one question. I am using Apache CXF framework to generate model classes from xsd. It works ok but now I have large CSV file used for model with a lot of attributes (around 20) and I want to validate every of this atrributes before sending them to some api method (saving to a database). Exists there some way how could I do it in my backing bean? Or somewhere else? I did not find any tutorial or example...
From How can I turn on schema validation for jaxws endpoint:
<jaxws:client name="{http://apache.org/serivce}SoapPort" createdFromAPI="true">
<jaxws:properties>
<entry key="schema-validation-enabled" value="true" />
</jaxws:properties>
</jaxws:client>