I have a service which calls a dozen other services. This reads from a Kafka topic using a @StreamListener
in a controller class. For traceability purposes, the same headers(original request ID) from the Kafka message need to be forwarded to all the other services as well
Traditionally, with a @PostMapping("/path")
or GetMapping
, a request context is generated, and one can access the headers from anywhere using RequestContextHolder.currentRequestAttributes()
and I would just pass the HttpHeaders
object into a RequestEntity
whenever I need to make an external call
However in a StreamListener
, no request context is generated and trying to access the RequestContextHolder
results in an exception
Here's an example of what I tried to do, which resulted in an exception:
public class Controller {
@Autowired Service1 service1
@Autowired Service2 service2
@StreamListener("stream")
public void processMessage(Model model) {
service1.execute(model);
service2.execute(model);
}
}
public class Service {
RestTemplate restTemplate;
public void execute(Model model){
// Do some stuff
HttpHeaders httpHeaders = RequestContextHolder.currentRequestAttributes().someCodeToGetHttpHeaders();
HttpEntity<Model> request = new HttpEntity(model, httpHeaders);
restTemplate.exchange(url, HttpMethod.POST, request, String.class);
}
}
My current workaround is to change the StreamListener
to a PostMapping
and have another PostMapping
which calls that so a request context can be generated. Another option was to use a ThreadLocal
but it seems just as janky
I'm aware of the @Headers MessageHeaders
annotation to access the stream headers, however, this isn't accessible easily without passing the headers down to each and every service and would affect many unit tests
Ideally, I need a way to create my own request context (or whatever the proper terminology is) to have a place to store request scoped objects (the HttpHeader
) or another thread safe way to have request headers passed down the stack without adding a request argument to service.execute