Sending message to a changing number of endpoints using Camel http4 - java

I have created the following piece of code and, instead of just one endpoint, I want to send the same JSON body to multiple endpoints using camel HTTP4. The number of endpoints is likely to change over time.
.choice()
.when(simple("${body} != null"))
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader(Exchange.HTTP_URI, simple(url))
.setProperty("ObjectBody", body())
.to("http4://someEndpoint?throwExceptionOnFailure=true&httpClient.socketTimeout=300000")
.otherwise()
.log(LoggingLevel.ERROR, "Incoming request has empty body")
.endChoice()
.end()
I'm trying to figure out the best way to configure and use multiple endpoints in this scenario.
I have looked at the Multicast EIP, but it looks like I would have to know the number of endpoints beforehand.
Any ideas are greatly appreciated!

The (dynamic in your case) recipientList is what you need !
See https://camel.apache.org/components/3.14.x/eips/recipientList-eip.html

Did you take a look at RouteBox?
THE NEED FOR A CAMEL ROUTEBOX ENDPOINT The routebox component is
designed to ease integration in complex environments needing a large
collection of routes and involving a wide set of endpoint technologies
needing integration in different ways
In such environments, it is often necessary to craft an integration
solution by creating a sense of layering among camel routes
effectively organizing them into
Coarse grained or higher level routes - aggregated collection of inner
or lower level routes exposed as Routebox endpoints that represent an
integration focus area. For example
https://camel.apache.org/components/2.x/routebox-component.html
Its usage:
from ("direct:sendToMapBasedRoutebox")
.setHeader("ROUTE_DISPATCH_KEY", constant("addToCatalog"))
.to("routebox:multipleRoutes?innerRegistry=#registry&routeBuilders=#routes&dispatchMap=#map")
.to("log:Routes operation performed?showAll=true");
Then in theory this should fan out the requests towards all (routes) encapsulated by a RouteBox.

Related

Auto create KafkaListeners

I work with apache-kafka and web flux (spring boot) and I want to know if there is a method to auto create a KafkaListener for each topic I add in application.yml(or properties)
This is not what consumer is for. The Kafka topic is a stream of data constantly changing . What is business purpose of that http request? Maybe you want to stream such a topic request to the Flux? Then consider to use Spring Integration dynamic flows and its toReactivePublisher() feature:
https://docs.spring.io/spring-integration/docs/current/reference/html/dsl.html#java-dsl-runtime-flows
https://docs.spring.io/spring-integration/docs/current/reference/html/reactive-streams.html#java-dsl
This sample shows something about Kafka and dynamic flows: https://github.com/spring-projects/spring-integration-samples/tree/main/dsl/kafka-dsl.
Also this one demonstrates some “to WebFlux” technique : https://github.com/artembilan/sandbox/tree/master/amqp-to-webflux.
Or you can look into Reactor Kafka: https://projectreactor.io/docs/kafka/release/reference/.

How to add matchOnUriPrefix on existing Camel Jetty REST routes?

We have existing REST routes working with Camel 2.23.1 and jetty. We redirect incoming calls to an appropriate server based on the uri, query, and the user's authentication. We want to handle this more generally.
How can we modify the following code to handle any uri with "/say" as the prefix?
In our RouteBuilder:
RestConfigurationDefinition rConfig = restConfiguration()
.component("jetty")
.port(webserverPort)
.contextPath("/")
.bindingMode(RestBindingMode.off)
.enableCORS(true)
.dataFormatProperty("prettyPrint", "true");
rest("/say")
.get().to("direct:test");
from("direct:test")
.bean(RouteRest.class, "getTestURI(*,*)")
.to("mock:output");
We have tried adding a property to the restConfiguration, ala
.componentProperty("matchOnUriPrefix", "true");
We have tried adding the same property to the rest route definition, ala
rest("/bye?matchOnUriPrefix=true")
We have tried creating a new from statement, which seems to break everything, ala
from("jetty://0.0.0.0:8123/now?matchOnUriPrefix=true").to("direct:test");
I am aware of this question and answer, but don't know how to apply it to my case:
stackoverflow.com/questions/39341784
Further, is it possible to match some incoming calls with explicitly defined uri's, like "/admin/status", and all other uri's to "direct:test"?
We ended up taking out the restConfiguration() entirely and configuring endpoints individually, which fit our expanding requirements anyway. Our oritinal restConfiguration() was limiting the messages that could get to the endpoints themselves. Perhaps we could have modified the restConfiguration directly to enable greater flexibility, including removal of .contextPath("/"). This directly allowed the following code to work:
from("jetty:http://{{ip}}:{{port}}?matchOnUriPrefix=true")
.bean(RestForward.class, "checkUserAuth(*)")
.bean(RestForward.class, "checkDevice(*)")
.bean(RestForward.class, "forward(*,*)")
.to("mock:output");

Programmatically created Feign client and Eureka target

I have started a few days ago to learn about fault tolerance solutions in microservices. I have some microservices in my ecosystem and they are now interconnected with Eureka service lookup. I used FeignClient to call from one to another. As I heard and read, that Hystrix is getting into maintenance, I wondered if I could use Resilience4J in Feign instead of Hystrix. Well, at least not from annotation level right now as it seems. I found a great Feign.Builder adapter to add resilience4j fault tolerance features above a FeignClient as a decorator (https://github.com/resilience4j/resilience4j/tree/master/resilience4j-feign) so I wanted to use it.
So I used this, added together the features and added the default encoder, decoder, etc. items into the feign builder. Turns out I have to finish of course my code with a .target call which creates my client proxy and I could not really do this with Eureka in a good way:
The first constructor, which takes the class type and the URL is hardcoded, so if I add an eureka next server query into this parameter, it is just a hardcoded url for one of the instances, this is not load balanced. Some kinda workaround could be that I create prototype-scope or similar short lived scoped beans of this client and always get the "next url" for the call. This adds lots of burden to use the clients in every class I make. At least as I saw it. Maybe I could add some kind of singleton helper bean around the prototyping, but again this is not a good design as I see
I thought maybe I could create an EurekaTarget from the Target interface, but of course none of the methods indicate any "end of lifecycle" things, not even the apply method. I thought maybe that is one point which is called before doing a service call, but I saw multiple calls towards it so I had to change the url for all calls.
Do you know any better solution to do this migration?
I guess you are using Spring Boot?
The next version v1.0.0 of Resilience4j will support the #FeignClient annotation.
There was a PR which added the functionality -> https://github.com/resilience4j/resilience4j/pull/579
You can then use it as follows:
#FeignClient(name = DUMMY_FEIGN_CLIENT_NAME)
#CircuitBreaker(name = DUMMY_FEIGN_CLIENT_NAME)
public interface DummyFeignClient {
String DUMMY_FEIGN_CLIENT_NAME = "dummyFeignClient";
#GetMapping(path = "/api/{param}")
void doSomething(#PathVariable(name = "param") String param);
}

How should I build my Messages in Spring Integration?

I have an application I coded which I am refactoring to make better use of Spring Integration. The application processes the contents of files.
The problem (as I see it) is that my current implementation passes Files instead of Messages, i.e. Spring Integration Messages.
In order to avoid further rolling my own code, which I then have to maintain later, I'm wondering if there is a recommended structure for constructing Messages in Spring Integration. What I wonder is if there is some recommended combination of channel with something like MessageBuilder that I should use.
Process/Code (eventually)
I don't yet have the code to configure it but I would like to end up with the following components/processes:
Receive a file, remove header and footer of the file, take each line and convert it into a Message<String> (This it seems will actually be a Splitter) which I send on to...
Channel/Endpoint sends message to Router
Router detects format String in Payload and routes to the appropriate channel similar to Order Router here...
Selected channel then builds appropriate type of Message, specifically typed messages. For example I have the following builder to build a Message...
public class ShippedBoxMessageBuilder implements CustomMessageBuilder {
#Override
public Message buildMessage(String input) {
ShippedBox shippedBox = (ShippedBox) ShippedBoxFactory.manufactureShippedFile(input);
return MessageBuilder.withPayload(shippedBox).build();
}
...
Message is routed by type to the appropriate processing channel
My intended solution does seem like I've complicated it. However, I've purposefully separated two tasks 1) Breaking a file into many lines of Messages<String> and 2) Converting Messages<String> into Messages<someType>. Because of that I think I need an additional router/Message builder for the second task.
Actually, there is MessageBuilder support in the Spring Integration.
The general purpose of such Frameworks is to help back-end developers to decouple their domain code from messaging infrastructure. Finally, to work with Spring Integration you need to follow the POJO and Method Invocation principles.
You write your own services, transformers and domain models. Then you just use some out of the box compoenents (e.g. <int-file:inbound-channel-adapter>) and just refer from there to your POJOs, but not vise versa.
I recommend you to read Spring Integration in Action book to have more pictures on the matter.
Can you explain the reason to get deal with Spring Integration components directly?
UPDATE
1) Breaking a file into many lines of Messages
The <splitter> is for you. You should write some POJO which returns List<String> - the lines from your file without header and footer. How to read lines from File isn't a task of Spring Integration. Especially, if the "line" is something logical, not the real file line.
2) Converting Messages into Messages
One more time: there is no reason to build Message object. It's just enough to build new payload in some transformer (again POJO) and framework wrap to its Message to send.
Payload Type Router speaks for itself: it checks a payload type, but not Message type.
Of course, payload can be Message too, and even any header can be as well.
Anyway your Builder snapshot shows exactly a creation of plain Spring Integration Message in the end. And as I said: it will be enough just to transform one payload to another and return it from some POJO, which you will use as a transformer reference.

Apache Camel - Multicast - Is there a 'null' or a similar endpoint in Camel?

Please excuse stupidity as this is my first Camel application
To respond to a web request, I am sourcing the content from two different sources.
I am, therefore, making a multicast request to two methods and parallelizing it.
The response is an marshalled JSON object (using camel-jackson)
All works fine.
public class RestToBeanRouter extends RouteBuilder{
#Override
public void configure() throws Exception {
from("cxfrs://bean://rsServer")
.multicast()
.parallelProcessing()
.aggregationStrategy(new CoreSearchResponseAggregator())
.beanRef("searchRestServiceImpl", "firstMethod")
.beanRef("searchRestServiceImpl", "secondMethod")
.end()
.marshal().json(JsonLibrary.Jackson)
.to("log://camelLogger?level=DEBUG");
}
Question :
The Multicast routing expects a to in the DSL. Currently, I am mapping this to a log endpoint. Is this fine?
Since I am not using the to and the last exchange of the Aggregator strategy is the one which is returned to the user, should my endpoint be configured to something else - like a null or something? (Ah, the stupidity kicks in)
For the benefit of SO visitors, copying the solution given in the Camel mailing list here :
by Robert Simmons Jr. MSc. - Lead Java Architect # EA
Author of: Hardcore Java (2003) and Maintainable Java (2012)
The aggregated exchange is the one that gets returned and how the
aggregated exchange is created depends on the aggregation strategy you use.
When a route stops either by calling stop or merely not routing anymore,
the exchange on the last part of the route could be considered a reply. In
most cases it will reply back to the caller (unless you set a reply-to
destination in a JMS based route or some other cases). In your case if all
you want to do is return the enriched exchange then you dont need any to()
call. Just stop after the marshal.

Categories

Resources