I have one local spring boot application which is hitting another PCF deployed application. The PCF application is giving me list of Student.
I am hitting the same using:
Observable<Response> result = RxObservable.newClient()
.target(url)
.request()
.rx()
.get();
Now I am not able to get my List<Student> back from result Observable.
Tried lots of approaches but nothing really working i.e. subscribing to result etc etc.
So after struggling for few hours below is the solution for now.
Observable observable = result.map(response -> response.readEntity(List.class));
DeferredResult<ResponseEntity<Response>> deferredResult = new DeferredResult(Duration.ofMillis(10000L).toMillis());
observable.subscribe((response) -> {
ResponseEntity<Response> responseData = new ResonseEntity(response, HttpStatus.OK);
deferredResult.setResult(responseData);
}, deferredResult::setErrorResult);
Please suggest, if it can be improved.
Thanks
Related
I have a springboot API which is dealing with lot of processes in backend. I need to stream the status to the frontend. Since I am new to springboot can anyone help me how to achieve this scenario.
Note - Application is going to be containerized in future and I cannot use any cloud service for this.
As there is not much to go off of I will try my best:
If you are using Log4j2 you could simply usethe SocketAppender (external link)
If not:
I did something similar recently and you will need to somehow turn your logs into a stream. I'd advise using the information found here (non-external link)
OutputStream Stream;
#GetMapping("/stream-sse-mvc")
public SseEmitter streamSseMvc() {
SseEmitter emitter = new SseEmitter();
ExecutorService sseMvcExecutor = Executors.newSingleThreadExecutor();
sseMvcExecutor.execute(() -> {
try {
Stream.map(sequence -> SseEmitter.event()
.id(""))
.event("EVENT_TYPE")
.data("String.valueOf(sequence)
.build());
emitter.send(event);
Thread.sleep(1000); //This does not need to be here
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
return emitter;
}
There might be better ways to reach your endpoints but without knowing what Frameworks you are using this is hard to answer. Essentially what we are doing is capturing all log output to a stream which is then broadcasted by an SSE.
I want to be able to extract the List<Payload> from the Mono<List<Payload>> to pass it to a downstream service for processing (or maybe return from the read(RequestParams params) method, instead of it returning void):
#PostMapping("/subset")
public void read(#RequestBody RequestParams params){
Mono<List<Payload>> result = reader.read(params.getDate(), params.getAssetClasses(), params.getFirmAccounts(), params.getUserId(), params.getPassword());
....
}
where reader.read(...) is a method on an autowired Spring service utilizing a webClient to get the data from external web service API:
public Mono<List<Payload>> read(String date, String assetClasses, String firmAccounts, String id, String password) {
Flux<Payload> nodes = client
.get()
.uri(uriBuilder -> uriBuilder
.path("/api/subset")
.queryParam("payloads", true)
.queryParam("date", date)
.queryParam("assetClasses", assetClasses)
.queryParam("firmAccounts", firmAccounts)
.build())
.headers(header -> header.setBasicAuth("abc123", "XXXXXXX"))
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> {
System.out.println("4xx error");
return Mono.error(new RuntimeException("4xx"));
})
.onStatus(HttpStatus::is5xxServerError, response -> {
System.out.println("5xx error");
return Mono.error(new RuntimeException("5xx"));
})
.bodyToFlux(Payload.class);
Mono<List<Payload>> records = nodes
.collectList();
return records;
}
Doing a blocking result.block() is not allowed in WebFlux and throws an exception:
new IllegalStateException("block()/blockFirst()/blockLast() are blocking, which is not supported in thread ..." ;
What is a proper way to extract the contents of a Mono in WebFlux?
Is it some kind of a subscribe()? What would be the syntax?
Thank you in advance.
There is no "proper way" and that is the entire point. To get the value you need to block, and blocking is bad in webflux for many reasons (that I won't go into right now).
What you should do is to return the publisher all the way out to the calling client.
One of the things that many usually have a hard time understanding is that webflux works with a producer (Mono or Flux) and a subscriber.
Your entire service is also a producer, and the calling client can be seen as the subscriber.
Think of it as a long chain, that starts at the datasource, and ends up in the client showing the data.
A simple rule of thumb is that whomever is the final consumer of the data is the subscriber, everyone else is a producer.
So in your case, you just return the Mono<List<T> out to the calling client.
#PostMapping("/subset")
public Mono<List<Payload>> read(#RequestBody RequestParams params){
Mono<List<Payload>> result = reader.read(params.getDate(), params.getAssetClasses(), params.getFirmAccounts(), params.getUserId(), params.getPassword());
return result;
}
While the following does return the value of the Mono observable in the logs:
#PostMapping("/subset")
#ResponseBody
public Mono<ResponseEntity<List<Payload>>> read1(#RequestBody RequestParams params){
Mono<List<Payload>> result = reader.read(params.getDate(), params.getAssetClasses(), params.getFirmAccounts(), params.getUserId(), params.getPassword());
return result
.map(e -> new ResponseEntity<List<PayloadByStandardBasis>>(e, HttpStatus.OK));
}
the understanding I was seeking was a proper way to compose a chain of calls, with WebFlux, whereby a response from one of its operators/legs (materialized as as a result from a webclient call, producing a set of records, as above) could be passed downstream to another operator/leg to facilitate a side effect of saving those records in a DB, or something to that effect.
It would probably be a good idea to model each of those steps as a separate REST endpoint, and then have another endpoint for a composition operation which internally calls each independent endpoint in the right order, or would other design choices be more preferred?
That is ultimately the understanding I was looking for, so if anyone wants to share an example code as well as opinions to better implement the set of steps described above, I'm willing to accept the most comprehensive answer.
Thank you.
Im getting an error with the blocking operation in Spring Webflux. I retrieve a Mono of list of Address documents and im using this Mono list of address documents to form the street address(withStreet)as shown below :
Mono<List<Address>> docs = getAddress(id, name);
AddressResponse addrResponse = new AddressResponse.Builder().
withStreet(docs.map(doc -> doc.stream().
map(StreetAddress::map).
collect(Collectors.toList())).block()).
build();
map method :
public static StreetAddress map(Address addr) {
return new Builder().
withId(addr.getId()).
withStreet(addr.getStreetAddress()).
build();
}
When i execute the above code, it throws a "block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2". Could you please suggest how to fix. i want to retrieve AddressResponse without blocking it. This response will be further used in the code in Response Entity as shown below :
return Mono.just(ResponseEntity
.status(HttpStatus.OK)
.body(addrResponse)));
The problem is you try to mix reactive and imperative code.
Instead, just map it in the reactive pipeline:
Mono<AddressResponse> response = docs.map(addresses->{
return new AddressResponse.Builder()
.withStreet(addresses -> addresses.stream()
.map(StreetAddress::map)
.collect(Collectors.toList()))
.build();
})
Then you can return it as is, or map it into a Mono> type, apply the same method then above.
I am developing corda application using kotlin. I am on webserver spring boot. My requirment is to recieve(postman or any API from outside) XML in requestBody and create IOU(pass) that xml as it is to Flow but i am not able to so. when i define it as a string and pass that xml i am able to do it. but for xml i am struggling. Can anyone help please. Below is my code. am doing anything wrong here. The problem is- i dont get error but it just doesnt work.
#PostMapping(value = ["createTransaction"],consumes = [MediaType.APPLICATION_XML_VALUE],produces = [ MediaType.TEXT_PLAIN_VALUE])
private fun TransactionOne(#RequestBody ()employee:Document, #RequestParam(value = "payload") payload: String, #RequestParam(value = "partyName") partyName: String): ResponseEntity<String> {
val partyX500Name = CordaX500Name.parse(partyName)
val otherParty = proxy.wellKnownPartyFromX500Name(partyX500Name) ?: return ResponseEntity.badRequest().body("Party named $partyName cannot be found.\n")
return try {
val signedTx = proxy.startTrackedFlow(::IOUFlow, employee, otherParty).returnValue.getOrThrow()
ResponseEntity.status(HttpStatus.CREATED).body("Transaction id {$signedTx} committed to ledger.\n")
} catch (ex: Throwable) {
logger.error(ex.message, ex)
ResponseEntity.badRequest().body(ex.message!!)
}
}
This is not clear what does it mean "just doesn't work". You don't have response from "startTrackedFlow"?
What do you see in logs of your nodes? I think the answer is there.
There is not enough information to help you...
New Spring has some WebSocketClient example on Spring documentation.
WebSocketClient client = new ReactorNettyWebSocketClient();
client.execute("ws://localhost:8080/echo"), session -> {... }).blockMillis(5000);
But it is very short and not clear:
How to send a message to the server (subscribe to a channel)?
Then handle incoming stream and emit Flux messages?
Reconnect to the server when the connection is interrupted?
Could some one provide more complex example?
UPD.
I tried to do something like:
public Flux<String> getStreaming() {
WebSocketClient client = new ReactorNettyWebSocketClient();
EmitterProcessor<String> output = EmitterProcessor.create();
Flux<String> input = Flux.just("{ event: 'subscribe', channel: 'examplpe' }");
Mono<Void> sessionMono = client.execute(URI.create("ws://api.example.com/"),
session -> session
.send(input.map(session::textMessage))
.thenMany(session.receive().map(WebSocketMessage::getPayloadAsText).subscribeWith(output).then())
.then());
return output.doOnSubscribe(s -> sessionMono.subscribe());
}
But that returns only one message. Like I didnt get subscription.
I assume you are using an "echo" service. In order to get some messages from the service, you have to push them into the websocket and the service will "echo" them back to you.
In your example code you are writing only a single element to the websocket. As soon as you push more messages into the socket you will get more back.
I adapted the code to connect to ws://echo.websocket.org instead of a local service. When you browse to /stream you see every second a new message appear.
#GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getStreaming() throws URISyntaxException {
Flux<String> input = Flux.<String>generate(sink -> sink.next(String.format("{ message: 'got message', date: '%s' }", new Date())))
.delayElements(Duration.ofSeconds(1));
WebSocketClient client = new ReactorNettyWebSocketClient();
EmitterProcessor<String> output = EmitterProcessor.create();
Mono<Void> sessionMono = client.execute(URI.create("ws://echo.websocket.org"), session -> session.send(input.map(session::textMessage))
.thenMany(session.receive().map(WebSocketMessage::getPayloadAsText).subscribeWith(output).then()).then());
return output.doOnSubscribe(s -> sessionMono.subscribe());
}
Hope this helps...
The documentation link above is to the temporary docs from before Spring Framework 5 was released. Currently the reference provides more information about implementing a WebSocketHandler.