-1

I'm learning more about using Spring Webflux and experimenting with testing a simple async webservice call. I've looked at several examples and I can't see what I'm doing wrong. I have a service that makes a call to a third party API and all I want to do is output the Json response returned. I'm not converting the response into model objects just yet but this will be the next step if I can get basics working first. The code doesn't log any of the output of the webservice call and I've also tried sending to System.out::println and that also doesn't work. The output in the test only includes the following log output

023-01-04 00:53:46.622 INFO 19938 --- [ main] c.r.io.service.impl.ListlyServiceImpl : Starting call to Listly API

2023-01-04 00:53:52.395 INFO 19938 --- [ main] c.r.io.service.impl.ListlyServiceImpl : Exiting service call to Listly

However , when I put a break point on

listlyResponse.subscribe(listlyResp ->
                log.info(listlyResp));

I can actually see the correct contents of the response from the web service call. Any ideas on what I'm doing wrong? This is the code

@Service
public class ListlyServiceImpl implements ListlyService {

    private final static Logger log = LoggerFactory.getLogger(ListlyServiceImpl.class);
    private final String baseUrl = "https://list.ly/api/v4";

    @Override
    public void callListlyService(String searchUrl) {
        if (searchUrl == null) {
            throw new RuntimeException("Search URL cannot be null");
        }
        log.info("Starting call to Listly API");
        Mono<String> listlyResponse = WebClient.create(baseUrl)
            .get()
            .uri(uriBuilder -> uriBuilder
                    .path("/meta")
                    .queryParam("url","{url}")
                    .build(searchUrl))
            .retrieve()
            .bodyToMono(String.class);

        listlyResponse.subscribe(listlyResp ->
                log.info(listlyResp));

//        listlyResponseFlux.subscribe(System.out::println);

        log.info("Exiting service call to Listly");
    }
}

I'm expecting to be able to output the contents of the web service call to the log output which is not working for some reason.

Lex Li
  • 60,503
  • 9
  • 116
  • 147
Sonik
  • 1
  • Please read every tags before using them. For example, the mono tag is not for your Java question. – Lex Li Jan 04 '23 at 01:58

1 Answers1

0

It is not clear without seeing your entire project, because I cannot see where you actually call your callListlyService() method. But I can see you're calling it from Main thread.

I think you misunderstood the reactive paradigm. As soon as you call subscribe() on reactive chain you should know that within the chain may happen the thread switching (depending on operators on your chain/some reactive API that may do thread switching) In case of WebClient the retrive() subscribes from netty threads by default and the next executions will happen on reactor-%s thread

So when you call your service method, it makes subsribe() and returns immediately showing you log "Exiting service call to Listly" and your method returns immediately, so you cannot see result of your call. Just to see your result you can call block() on your reactive chain, that will force your calling thread to block and waiting the response from your WebClient. But this approach is not recommended in reactor because you're losing benefits from reactive-way. In real cases you should return Publisher<> from your method and , for example, subscribe by yourself where you need/use Spring WebFlux returning Publishers in your controller methods leaving subsribe process to WebFlux

One another way just for experimanetal purposes: you can place Thread.sleep(n) in the end of your test, where n - time in millis for your Main thread to wait. The time should be greater than your actual web-call

kerbermeister
  • 2,985
  • 3
  • 11
  • 30
  • You can also use StepVerifier from dependency reactor-test to verify the results of your reactive pipeline – kerbermeister Jan 04 '23 at 23:30
  • Thanks for your response. At the moment as this is just experimenting I only call the service from a Spring Boot test. I did try putting a sleep in at the end of the method to see if this would work however I don't think I put the sleep in for long enough. I just tried it again with 10 secs and I can now see it output the response. So if I have a number of 3rd party web service calls I want to make and then have a mechanism to process the results which will use the outputs of each to perform the processing required whats the best way to do that in a service – Sonik Jan 04 '23 at 23:33
  • Your question is quite abstract, what I can say, for example, if you have 3 different methods that call different external API and each return Mono, the you can use Mono.zip() to aggregate your calls, make them execute concurrently, and then handle thre aggragated result from them downstream of your pipeline – kerbermeister Jan 04 '23 at 23:40