How to create a custom Apache Camel Timer object - java

Is there a way create a custom Apache Camel Timer as an object defined in java code rather than defining it as a dsl string pattern in the endpoint URI?
In the docs: https://camel.apache.org/components/latest/timer-component.html there is mention of this timer URI query param:
but I haven't found examples of creating that "advanced" custom Timer.
Rather than specify a timer like this:
from("timer://foo?fixedRate=true&period=60000").to("bean:myBean?method=someMethodName");
I would like to specify it as:
from("timer://foo?timer=com.MyCustomTimer").to("bean:myBean?method=someMethodName");
which would be accompanied by:
class MyCustomTimer implements TimerInterfaceICantFind{
public MyCustomTimer(){
setFixedRate(true);
setPeriod(60000);
}
}
I'm wanting to do this so I can specify timer properties dynamically through java setters rather than substituting them into a string URI being constructed.
At the time of this writing, others have asked of Timer URI string syntax, but not of custom timers. Ex:
Apache Camel timer route URI syntax
I saw in the camel source code, it looks like TimerComponent.getTimer() is returning a java.util.Timer
Would that imply that one needs to create the core java class: java.util.Timer - and set properties on it rather than using a camel version of a Timer object for a custom Timer?

The custom Timer that's referrred to in the camel docs is not a camel object but rather a java.util.Timer object. It is lower level and not user friendly to work with manually in camel routes. It's not recommended to use.
You can't instantiate a camel TimerEndpoint object and use setters for its values without also manually specifying the endpoint string uri.
Without providing that uri - camel throws an exception with the message:
"endpointUri is not specified and org.apache.camel.component.timer.TimerEndpoint does not implement createEndpointUri() to create a default value".
If using camel 3+ there is an endpoint dsl option available, EndpointRouteBuilder, that can create a timer endpoint through a builder:
RouteBuilder.addRoutes(camelContext, rb ->
rb.from(timer("myTimer").fixedRate(true).period(500).advanced().synchronous(false))
.delay(simple("${random(250,1000)}"))
.process(this::newOrder)
.to(direct("cafe")));
https://github.com/apache/camel-examples/blob/master/examples/camel-example-cafe-endpointdsl/src/main/java/org/apache/camel/example/cafe/CafeRouteBuilder.java#L69

If you insist to have the timer generated dynamically, perhaps you can take a look at Camel EndpointDSL which allows you to construct endpoints dynamically. Here is for timer and an example to use it.

Related

gRPC - Use of ContextPropagatingExecutorService and currentContextExecutor

Since gRPC makes service call on new thread and gRPC context is Thread Local, how can I propagate this gRPC context? I found that Context.currentContextExecutor() and ContextPropagatingExecutorService can be used but I haven't found enough resources or example for these 2 options. Can someone help to implement these?
ClientInterceptors shouldn't change the context instance seen by the application. The Context behavior shouldn't really change whether using blocking, async, or future stubs and a blocking API would not be able to change the current context.
While an interceptor is free to modify a pre-existing (mutable) value in the Context, there's generally little need. It is normally easier to create a new interceptor instance each RPC and communicate with the interceptor directly, or communicate via a custom CallOption.
If you have just a single call site that needs access to response headers, then MetadataUtils.newCaptureMetadataInterceptor() is a convenient (although roundabout) way to get the Metadata. It was designed for testing, but is appropriate for small-scale use outside of testing situations.
AtomicReference<Metadata> headers = new AtomicReference<>();
AtomicReference<Metadata> trailers = new AtomicReference<>();
// Using blocking for simplicity, but applies equally to futures
stub.withInterceptors(MetadataUtils.newCaptureMetadataInterceptor(headers, trailers))
.someRpc();
Metadata headersSeen = headers.get();
If you need to access the same header from multiple callsites, it is better to create a custom interceptor that does what you need.
CustomInterceptor interceptor = new CustomInterceptor();
stub.withInterceptors(interceptor)
.someRpc();
... = interceptor.getWhateverValue();
This is demonstrating a "general" use case. Specific instances commonly can tweak their API further to be more convenient and natural.

Create a not connected PublishSubscribeChannel with subflows

Long story short: I need to have something like below.
PublishSubscribeChannel firstChannel = new PublishSubscribeSpec(executor).subscribe(subFlow -> ...).get();
Is there a way to create a pubsub channel with subflows which is not (yet) connected to any other flow?
The snippet is not working because of PublishSubscribeSpec(Executor) has protected access in PublishSubscribeSpec.
I will need to register channels like this dynamically without any information about which flow(s) will be using these channels.
has protected access in PublishSubscribeSpec
That was exactly a reason to make it protected - to avoid an unusual configuration problem like your. The subflow cannot be provided like this in the plain PublishSubscribeChannel definition. It is part of Java DSL parser in the framework to determine such a configuration and register respective beans in the application context. With that explicit get() call you just fully eliminate a hook for Java DSL parser to understand your configuration.
without any information about which flow(s) will be using these channels.
That's not true according your .subscribe(subFlow -> intention. Adding a subflow to the PublishSubscribeSpec is indeed "an information which flow will be using these channel".
Perhaps we need to look into your business requirement from another angle. There is no reason to be stuck with subflows approach when we simply can use a PublishSubscribeChannel from any other place where a MessageChannel is needed as an input. I mean if you just create a plain PublishSubscribeChannel and then use it for example for the IntegrationFlows.from(MessageChannel) factory, you'll get the same runtime result as you would expect from those .subscribe(subFlow -> connections.

Apache Camel - How is the method name selected for invocation in this bean?

I am just starting out with Apache Camel and I am confused as to how Camel is selecting which method to invoke in the following code:
This a part of this YouTube video.
someBean is defined as :
#Singleton
public class SomeBean {
private int counter;
public String someMethod(String body); //Not providing the full definition as it is in the video.
}
According to the Camel documentation these rules are used to select a method for invocation, but I am unable to make out which rule is used.
How can Camel figure out which method to call just by the bean object ?
The described method selection mechanism in the Camel docs you mention is just the "top of the iceberg". There are even more ways to choose a method of a Bean.
If you want to learn the full blown Bean method selection mechanism, get a copy of Camel in Action by Claus Ibsen. Chapter 4.4 is dedicated to this subject and explains it on almost 10 pages with examples.
In your case I guess the matching rule is
Is there only one method with a single parameter?
If this is the case, Camel will use this method. You can try this very easily with a RouteTest. Create a route and a simple bean as in the video. When it works, start to add methods to the bean and look what method is chosen.

How do I set a component parameter of type object to a route using Java DSL?

My objective is to use Camel along with its JMS component.
The route config looks like below-
from("jms:queue:test").to(mybean)
I would like to add the option of kind 'parameter' and type 'object' to this route -for example the option 'jmsMessageType'.
I saw some other posts that talks about using setProperty() on route definition but I could not find a definite answer. Options of type 'string' and numbers can be appended to the URI but not objects.
JMS has an option of taskExecutor but how can i add an instance of this to URI for routing.
I think you confuse parameter with option.
jmsMessageType you are referring is an option of Camel's jms component. Each component can have many options and you can use them by appending with "?" character. For example
from("jms:queue:test?jmsMessageType=text").to(mybean)
More particular, for the jms component avalable options can be found in http://camel.apache.org/jms.html (see Common and Advanced Options sections)
Property is something different, it has nothing to do with the component, but with the Exchange message that is passed through the endpoints. More details are in Passing values between processors in apache camel
I had to resolve this by adding the instances to a custom registry and using them from the endpoint URI
From official Apache Camel page
From Camel 2.0:
When configuring endpoints using URI syntax you can now refer to beans
in the Registry using the # notation. If the parameter value starts
with a # sign then Camel will lookup in the Registry for a bean of the
given type. For instance:
file://inbox?sorter=#mySpecialFileSorter

How to modify a camel endpoint at runtime

The problem
I have a spring mvc application that uses apache camel. I am confused on the role that the RouteBuilder class plays and how it actually gets initialized. I know that the docs say that the configure() method is:
Called on initialization to build the routes using the fluent builder syntax.
but when does this initialization occur? Does it occur at application startup or some time later when the route is about to be used?
The purpose of this question is ultimately to ask how I can modify the route at runtime. I want to be able to build different routes as needed.
Examples
xml definitions:
<service name="myService" tier="3">
<requestType>my.package.RequestType</requestType>
<responseType>my.package.ResponseType</responseType>
<endpoint>
<httpEndpoint>
<url default="true" value="someUrl"/>
<timeout value="5000"/>
</httpEndpoint>
</endpoint>
</service>
Route Builder template:
public class myRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
// When does this method get executed?
}
}
Questions
When does configure() execute?
How can I dynamically set the endpoint url?
You are able to use toD to dynamically change the endpoint at runtime based on an expression. See the documentation
If you want to change more of the route or add a completely new route then look at the API on the CamelContext. This Stackoverflow question has an example of adding a completely new route.
The lifecycle of the Camel service is documented here : https://camel.apache.org/lifecycle.html
Camel uses a simple lifecycle interface called Service which has a single start() and stop() method.
Various classes implement Service such as CamelContext along with a number of Component and Endpoint classes.
When you use Camel you typically have to start the CamelContext which will start all the various components and endpoints and activate the routing rules until the context is stopped again.
It is when the context starts that the various components start. Not sure i understand the dynamic url part. If it is to indicate a dynamic endpoint (if the data is this , then queue1 else queue2) you should be able to use something like the DynamicRouter EIP which is as explained here (https://camel.apache.org/dynamic-router.html)
You have several options.
Inject them as spring properties.
Inject them from external properties source.
Inject them from some bean method.
Then you can put the property value in a header and later put the value in the .toD("$header.routeEndpoint"). This can take care of dynamic endpoints.
Off course to rebuild the entire route you need to play with the API.

Categories