Java Vavr : Log Exception on Failure - java

I want to log exceptions while using VAVR (formerly javaslang). Below is the sample code snippet.
//will get 500 as response from this url
String sampleurl = "http://someurl.com";
List<String> myList = List.of(sampleurl);
LOGGER.info("In {} with urls {}",getClass(),myList);
return Observable.from(myList).flatMap(url ->
Observable.create(subscriber -> {
Try<String> httpEntity = HttpUtil.retrieveData(url).flatMap(httpResponse -> Try.of( () -> EntityUtils.toString(httpResponse.getEntity())));
httpEntity
.andThen(subscriber::onNext)
.andThen(subscriber::onCompleted)
.onFailure(subscriber::onError);
}));
I am trying to log exception in the onFailure() block but nothing gets logged. Please advise me on this.
Regards,
Jai

In both cases, success and failure, Vavr works as expected. Here are simple tests:
// prints nothing
Try.success("ok")
.andThen(() -> {})
.andThen(() -> {})
.onFailure(System.out::println);
// prints "java.lang.Error: ok"
Try.failure(new Error("ok"))
.andThen(() -> {})
.andThen(() -> {})
.onFailure(System.out::println);
I see two possible answers why the failure is not logged in your example:
the logger configuration does not fit your needs
the observables are not processed and Try is never called
Disclamer: I'm the creator of Vavr

Related

webclient retrieve() vs exchangeToMono() nothing working for this use case

API (Name it as api-1) has following property -
For 2XX it can return body
For 5XX it doesn’t return body
In another API (api-2), Our requirement is if api-1 status code is 2XX return “ABC” else return “XYZ” from webclient call, So we don’t wanted to consume response body in either cases.
Which one should work retrieve() or exchangeToMono() ?
When I use retrieve() I couldn’t return ABC or XYZ based on http response. If I use exchangeToMono() I was getting InvalidStateException with following message “The underlying HTTP client completed without emitting a response.”
Any help is appreciated.
Here is my approach.
String result = client
.get()
.uri("http://localhost:8080/endpoint")
.retrieve()
.toBodilessEntity()
.onErrorResume(WebClientResponseException.class, e -> Mono.just(ResponseEntity.status(e.getStatusCode()).build()))
.map(entity -> entity.getStatusCode().isError() ? "error" : "ok")
.block();
result is equal to ok when the request is completed successfully, and error otherwise.
You can change the condition in map if you want to return error only on 5xx exceptions.
EDIT: Or as Toerktumlare mentioned in his comment, you can use onStatus to omit an exception.
String result = client
.get()
.uri("http://localhost:8080/endpoint")
.retrieve()
.onStatus(HttpStatus::isError, e -> Mono.empty())
.toBodilessEntity()
.map(entity -> entity.getStatusCode().isError() ? "error" : "ok")
.block();

How add or Ignoring a specific http status code in WebClient reactor?

I have a this method that manage all request of my project:
private Mono<JsonNode> postInternal(String service, String codPayment, String docNumber, RequestHeadersSpec<?> requestWeb) {
return requestWeb.retrieve().onStatus(HttpStatus::is4xxClientError,
clientResponse -> clientResponse.bodyToMono(ErrorClient.class).flatMap(
errorClient -> clientError(service, codPayment, clientResponse.statusCode(), docNumber, errorClient)))
.onStatus(HttpStatus::is5xxServerError,
clientResponse -> clientResponse.bodyToMono(ErrorClient.class)
.flatMap(errorClient -> serverError(service, codPayment, docNumber, errorClient)))
.onRawStatus(value -> value > 504 && value < 600,
clientResponse -> clientResponse.bodyToMono(ErrorClient.class)
.flatMap(errorClient -> otherError(service, codPayment, docNumber, errorClient)))
.bodyToMono(JsonNode.class);
}
But one of the API that i consume response with Status code 432 when the response is ok but have a special condition in it and i must show it but webClient show this error:
org.springframework.web.reactive.function.client.UnknownHttpStatusCodeException: Unknown status code [432]
at org.springframework.web.reactive.function.client.DefaultClientResponse.lambda$createException$1(DefaultClientResponse.java:220) ~[spring-webflux-5.2.1.RELEASE.jar:5.2.1.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ 432 from POST https://apiThatIConnectTo....
How can i avoid this and response normally? and is possible to add this status code to my JsonNode.class or a custom generic class that map the response and the status code or any ideas? thanks
From the java doc. (This also applies to the onRawStatus method)
To suppress the treatment of a status code as an error and process it as a normal response, return Mono.empty() from the function. The response will then propagate downstream to be processed.
An example code snippet would look like
webClient.get()
.uri("http://someHost/somePath")
.retrieve()
.onRawStatus(status -> status == 432, response -> Mono.empty())
.bodyToMono(String.class);

Inner list from DTO is null while testing with Spring WebFlux

I have the following unit test (JUnit 5):
FluxExchangeResult<CalendarDTO> calendarEntityResult = client.get()
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.returnResult(CalendarDTO.class);
assertNotNull(calendarEntityResult);
final Flux<CalendarDTO> responseBody = calendarEntityResult.getResponseBody();
responseBody.flatMap(calendarDTO -> {
assertNotNull(calendarDTO);
final List<AppointmentDTO> calendarEvents = calendarDTO.getCalendarEvents();
assertNotNull(calendarEvents);
assertFalse(calendarEvents.isEmpty());
return Flux.just(calendarEvents);
}).map(calendarEvents ->
calendarEvents.get(0)
).doOnNext(appointmentDTO ->
assertEquals(appointmentDTO, validAppointmentDTO())
).subscribe();
/*StepVerifier.create(responseBody)
.assertNext(calendarDTO -> {
assertNotNull(calendarDTO);
final List<AppointmentDTO> calendarEvents = calendarDTO.getCalendarEvents();
assertNotNull(calendarEvents);
assertFalse(calendarEvents.isEmpty());
final AppointmentDTO appointmentDTO = calendarEvents.get(0);
assertNotNull(appointmentDTO);
assertEquals(validAppointmentDTO(), appointmentDTO);
})
.expectComplete()
.verify();*/
For some reason, the assertNotNull(calendarEvents); is failing. The method itself when running it with Postman is fine. What has me puzzled is that on debug time, the calendarEntityResult has calendarEvents!
> GET /appointments
> WebTestClient-Request-Id: [1]
No content
< 200 OK OK
< Content-Type: [application/json;charset=UTF-8]
< Content-Length: [377]
{"data":{"calendarEvents":[{"id":null,"startTime":"2020-01-16T13:19:37.510-06:00","endTime":"2020-01-16T14:19:37.511-06:00","timeZoneStart":"America/Regina","timeZoneEnd":"America/Regina","summary":"unit test summary","description":"unit test description","organizerName":"Developer","organizerEmail":"developer#dev.com","status":null,"alarm":15}]},"notifications":null}
The commented code gives the same result. To be clear, the DTO itself is not null; the problem is the calendarEvents array. It's possible I'm doing something wrong since I'm new to reactive programming in general, so code improvements are most welcome. Am I extracting the data in a wrong manner?
You should be using the stepverifier when asserting any type of flux. Will make your life easier.
final Flux<String> responseBody = testClient.get()
.exchange()
.expectStatus()
.isOk()
.returnResult(String.class)
.getResponseBody();
StepVerifier.create(responseBody)
.assertNext(s -> assertEquals(s, "Foo"))
.assertNext(s -> assertEquals(s, "Bar"));
Turns out the root DTO was already wrapped into yet another DTO. It probably caused getResponseBody() to misinterpret the contents of the list and defaulted those to null. Leaving response here for the curious:
final Flux<AppointmentCalendarResponse> responseBody = client.get()
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.returnResult(AppointmentCalendarResponse.class)
.getResponseBody();
StepVerifier.create(responseBody)
.assertNext(data -> {
CalendarDTO calendarDTO = data.getData();
final List<AppointmentDTO> calendarEvents = calendarDTO.getCalendarEvents();
assertNotNull(calendarEvents);
})
.expectComplete()
.verify();
You're using the same DB?
Because generaly the DB test and DB Dev is different.

How reuse flux mono value?

Consider a code:
WebClient webClient = ... ;
public Mono<MyWrapper> someFunction () {
Mono<MyDto> mono = webClient.get()
.uri("myUrl")
.retrieve()
.bodyToMono(MyDto.class);
Mono<FirstDto> first = mono.map(dto -> {...});
Mono<SecondDto> second = mono.map(dto -> {...}); //<- connection closed error here
return Mono.zip(first, second).map(zip -> {
return new MyWrapper(first, second);
});
}
Second map operation leads to connection closed error. I suppose that flux tried to send new request. (Does it or not?)
Second is there a way to map mono twice: to one type and another one without sending new request?
Have you tried using compose?
Mono<MyWrapper> mono = webClient.get()
.uri("myUrl")
.retrieve()
.bodyToMono(MyDto.class)
.compose(dto -> dto
.zip(dto.map(dto -> {...}), dto.map(dto -> {...})))
.map(MyWrapper::new);
API:
https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#compose-java.util.function.Function-

Rxjava approach the get last Observable from each thread

I'm thinking how to use RXJava for the scenario described bellow.
A List<Object>,each object will be sent to k8s and checked the status till the respone return true,so my polling active is that:
private Observable<Boolean> startPolling(String content) {
log.info("start polling "+ content);
return Observable.interval(2, TimeUnit.SECONDS)
.take(3)
.observeOn(Schedulers.newThread())
.flatMap(aLong -> Observable.just(new CheckSvcStatus().check(content)))
.takeUntil(checkResult -> checkResult)
.timeout(3000L, TimeUnit.MILLISECONDS, Observable.just(false))
;
}
Function of sent action:
Observable<Compo> sentYamlAndGet() {
log.info("sent yaml");
sentYaml()
return Observable.just(content);
}
I try to use the foreach to get each object status which like this:
public void rxInstall() throws JsonProcessingException {
List<Boolean>observables = Lists.newArrayList();
Observable.from(list)
.subscribeOn(Schedulers.newThread())
.concatMap(s -> sendYamlAndGet())
.timeout(3000l, TimeUnit.MILLISECONDS)
.subscribe()
;
Observable.from(list).forEach(s -> {
observables.add(Observable.just(s)
.flatMap(this::startPolling)
.toBlocking()
.last()
)
;
System.out.println(new ObjectMapper().writeValueAsString(observables));
}
Objects of outputs list is :{"o1","o2","o3","o4","o5"}
the last status of objest which I want is : [false,true,false,false,true].
All above style is not much 'ReactX',check object status action do not affect to each other.
How to throw foreach? I trid toIterable(),toList() but failed.
Observable.from(list)
.concatMap(s -> sentYamlAndGet())
.concatMap(this::startPolling)
....
;
Wanted to know if it's good practice to do that and what would be the best way to do that?
Thanks in advance.
pps: currentlly I'm using rxjava1 <version>1.2.0</version> but can change to 2(´▽`)ノ

Categories

Resources