SpringBoot kafka request/reply with dynmaic topic name for #KafkaListener - spring

i am writing a microservices application based on kafka request/reply semantic, so i got configured ReplyingKafkaTemplate as message producer and #KafkaListener with #SendTo annotations method as the service request listener. I need to create a class with method marked with #KafkaListeners and #SendTo annotations dynamically based on topic name. It should be something like this:
#Component
class KafkaReceiver {
// LISTENER FOR OTHER MICROSERVICE REQUESTS
#KafkaListener("#{topicProvider.getTopic()})
#SendTo
public Response listen(Request request) {
... some logic
return response;
}
}
#Component
class KafkaRecevierFactory {
public KafkaReceiver createListener(String topic) {
...
return kafkaReceiver;
}
//OR SOMETHING LIKE THIS:
public void runListenerContainer(String topic, ReqeustProcessor processor) {
Container container = ContainerFactory.create(topic)
container.setListener( request -> {
Response resp = processor.process(request);
return resp;
});
container.start();
}
}
Is there anyway i can do this?

Related

FeignClient is passing on headers

I have about 10 microservices all built with Spring boot 2 using Eureka and FeignClients. My microservices use certain header values to keep track of data so when a FeignClient is used it needs to pass on certain values that are in the incoming request. So if Microservice 1 does a call to Microservice 2 it must pass on the headers from the incoming request onto microservice 2. I haven't been able to find out how I can do that. I understand their is #Header however if you have 20 FeignClients then you don't want to have to manually add the #header to all the FeignClients. Can you indicate that FeignClients must read a certain header from the incoming request and pass it on in the FeignClient?
You can use request interceptor in Feign.
Example Implementation:
Request Interceptor:
#Component
public class MyRequestInterceptor implements RequestInterceptor {
#Override
public void apply(RequestTemplate template) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
String authorization = requestAttributes.getRequest().getHeader(HttpHeaders.AUTHORIZATION);
if(null != authorization) {
template.header(HttpHeaders.AUTHORIZATION, authorization);
}
}
}
Bean Configuration:
#Configuration
public class CustomFeignConfig {
#Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
#Bean
public MyRequestInterceptor basicAuthRequestInterceptor() {
return new MyRequestInterceptor();
}
#Bean
public OkHttpClient client() {
return new OkHttpClient();
}
}

Intercepting Camel #Consume

I have an existing application which is using Apache Camel to send messages to SEDA endpoints for Async processing and would like to intercept calls to these methods for instrumentation.
Example code:
#Component
public class CamelMessageService {
private static final Logger log = LoggerFactory.getLogger(CamelMessageService.class);
public static final String QUEUE = "seda:message";
#Resource
private ProducerTemplate producerTemplate;
public void send() {
producerTemplate.sendBody(QUEUE, "Hello World");
}
#Consume(uri = QUEUE)
public void receive(#Body String payload) {
log.info("Received message {}", payload);
}
}
Is there a way to intercept all methods annotated with #Consume before invoking. I looked at an AOP based approach but this seemed to fall over due to existing Spring/Camel proxying of these classes.
I have also tried using various Camel Intercept routes and adding a custom InterceptStrategy but it seems that the example above does not create a Camel route so is not intercepted.
EDIT: On further investigation in seems that these endpoints can be Intercepted using camel but only if there is at least 1 other route defined in the Camel Context?
#Component
class MyRouteBuilder extends RouteBuilder {
private static final Logger log = LoggerFactory.getLogger(MyRouteBuilder.class);
public void configure() {
interceptSendToEndpoint(CamelMessageService.QUEUE)
.process(exchange -> log.info("intercepted exchange {}", exchange));
from("timer:hello?period={{timer.period}}").routeId("hello").routeGroup("hello-group")
.transform().simple("yo")
.filter(simple("${body} contains 'foo'"))
.to("log:foo")
.end()
.to("stream:out");
}
}
If I run this app with the Route Builder above then my interceptor is triggered if however I comment out the hello route it is not?
Any help would be greatly appreciated.

Message headers not included in error handling with Spring Integration DSL

I am trying to track all transactions adding extra headers on each operation, these extra headers work fine with request and response, but in error case no headers are included.
This is my configuration (with Spring Integration DSL and Java 1.7)
#Bean
public IntegrationFlow inboundFlow() {
return IntegrationFlows.from(Amqp.inboundGateway(simpleMessageListenerContainer())
.mappedReplyHeaders(AMQPConstants.AMQP_CUSTOM_HEADER_FIELD_NAME_MATCH_PATTERN)
.mappedRequestHeaders(AMQPConstants.AMQP_CUSTOM_HEADER_FIELD_NAME_MATCH_PATTERN)
.errorChannel(gatewayErrorChannel())
.requestChannel(gatewayRequestChannel())
.replyChannel(gatewayResponseChannel())
)
.transform(getCustomFromJsonTransformer())
.route(new HeaderValueRouter(AMQPConstants.OPERATION_ROUTING_KEY))
.get();
}
#Bean
public MessageChannel gatewayRequestChannel() {
return MessageChannels.direct().get();
}
#Bean
public MessageChannel gatewayResponseChannel() {
return MessageChannels.publishSubscribe().get();
}
#Bean
public MessageChannel gatewayErrorChannel() {
return MessageChannels.publishSubscribe().get();
}
#Bean
public IntegrationFlow responseTrackerOutboundFlow() {
return trackerOutboundFlowTemplate(gatewayResponseChannel());
}
#Bean
public IntegrationFlow errorTrackerOutboundFlow() {
return trackerOutboundFlowTemplate(gatewayErrorChannel());
}
private IntegrationFlow trackerOutboundFlowTemplate(MessageChannel fromMessageChannel) {
return IntegrationFlows.from(fromMessageChannel)
.handle(Amqp.outboundAdapter(new RabbitTemplate(getConnectionFactory()))
.exchangeName(LOGGER_EXCHANGE_NAME)
.routingKey(LOGGER_EXCHANGE_ROUTING_KEY)
.mappedRequestHeaders("*"))
.get();
}
I am using errorChannel for inboundGateway and also using mappedReplyHeaders and mappedRequestHeaders, is it possible to have headers in errorChannel? There is a way to configure mapped error headers or something like that?
The mappedReplyHeaders work only if you receive the good reply from the downstream flow. They are applied exactly before sending the reply message to the AMQP.
The errorChannel is a part of integration messaging, therefore no access to the mappedReplyHeaders at all. Forget them here!
From other side the errorChannel is responsible to wrap an Exception into the new ErrorMessage. That's why you don't see your headers there directly.
But you should bare in mind that integration messaging in most cases is MessagingException with the failedMessage property. That failedMessage is a "guilty" message for an exception.
And if the normal headers population process is done everywhere, you can get access to your headers from this failedMessage of the MessagingException payload of the ErrorMessage in the errorChannel flow.

Spring reactor executing consumers asynchronously

Hi I am trying out Spring reactor so far everything works but my consumers are invoked one after the other ..synchronously
My use case :
The events are published from a restful controller and two simple consumer which prints to console are invoked (In reality one consumer writes to DB and the other publishes the data to a web socket)
I am using spring boot 1.2.7
This is my controller
#Controller
public class MessageController {
#Autowired
#Qualifier("myreactor")
private Reactor r;
#RequestMapping("/hello/{username}")
public #ResponseBody String welcome(#PathVariable String username) {
r.notify("MessageCreated", Event.wrap(username));
return username;
}
}
My event handlers
Handler 1
#Consumer
public class MessageConsumer {
#Autowired
#Qualifier("myreactor")
private Reactor reactor;
#Selector("MessageCreated")
public void handleRequest(Event<String> evt) {
System.out.println("Called Handler 1 " +evt.getData());
}
}
Handler 2
#Consumer
public class MessageConsumer2 {
#Autowired
#Qualifier("myreactor")
private Reactor reactor;
#Selector("MessageCreated")
public void handleRequest(Event<String> evt) {
System.out.println("Called Handler 2 "+ evt.getData());
}
}
My reactor configuration
#Configuration
#EnableReactor
public class ReactorConfiguration {
#Bean
Environment env() {
return new Environment();
}
#Bean
#Qualifier("myreactor")
Reactor myreactor(Environment env) {
return Reactors.reactor()
.env(env)
.dispatcher(Environment.THREAD_POOL)
.get();
}
}
Whatever I do the result is always
Called Handler 1 Hello
Called Handler 2 Hello
Consumer are invoked in the same order
...please do let m know where did I mess up..The above is a mock implementation as I cant paste the original code here.

Apache Camel Spring Javaconfig Unit Test No consumers available on endpoint

I have the following route configuration:
#Component
public class MyRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct:in").to("direct:out");
}
}
When I try to test it:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyRouteTest.TestConfig.class }, loader = CamelSpringDelegatingTestContextLoader.class)
#MockEndpoints
public class MyRouteTest {
#EndpointInject(uri = "mock:direct:out")
private MockEndpoint mockEndpoint;
#Produce(uri = "direct:in")
private ProducerTemplate producerTemplate;
#Configuration
public static class TestConfig extends SingleRouteCamelConfiguration {
#Bean
#Override
public RouteBuilder route() {
return new MyRoute();
}
}
#Test
public void testRoute() throws Exception {
mockEndpoint.expectedBodiesReceived("Test Message");
producerTemplate.sendBody("Test Message");
mockEndpoint.assertIsSatisfied();
}
}
I get this exception:
org.apache.camel.component.direct.DirectConsumerNotAvailableException:
No consumers available on endpoint: Endpoint[direct://out].
Exchange[Message: Test Message]
It looks like the Mock is not picking up the message from the endpoint.
What am I doing wrong?
The problem is that mock endpoints just intercept the message before delegating to the actual endpoint. Quoted from the docs:
Important: The endpoints are still in action. What happens differently
is that a Mock endpoint is injected and receives the message first and
then delegates the message to the target endpoint. You can view this
as a kind of intercept and delegate or endpoint listener.
The solution to your problem is to tell certain endpoints (the ones that expect a consumer in your case) not to delegate to the actual endpoint. This can easily be done using #MockEndpointsAndSkip instead of #MockEndpoints:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyRouteTest.TestConfig.class }, loader = CamelSpringDelegatingTestContextLoader.class)
#MockEndpointsAndSkip("direct:out") // <-- turns unit test from red to green ;)
public class MyRouteTest {
// ....
}
This issue because, in your route configuration, there is no route with "direct:out" consumer endpoint.
add a line like some thing below,
from("direct:out").("Anything you want to log");
So that direct:out will consume the exchange and In your test, mock will be able check the received text without any issues. Hope this helps !!

Resources