I am in the middle of learning Spring WebFlux. I am using a REST call using below code to parse the response:
private void parseJsonResponse(String folderId) throws IOException {
Mono<ObjectNode> theresponseMono = webClient.get()
.uri("/some/uri")
.retrieve().bodyToMono(ObjectNode.class);
ObjectNode node = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.readValue(theresponseMono.share().block().toString(), ObjectNode.class);
//handle node object here.....
}
Question: Is theresponseMono.share().block() non-blocking here? If not, what can be done to make is completely non-blocking. I am looking for the relevant documentation on this as I want to learn it and not just looking for a yes or no. To summarize, I need to retrieve theresponseMono as non-blocking. Any guidance or any official documentation/link on this please? Thanks.
EDIT:
This is what I am trying to achieve:
Mono<ObjectNode> theresponseMono = webClient.get()
.uri("/some/uri")
.retrieve().bodyToMono(ObjectNode.class).flatMap(node -> {
if (node.get("list").get("entries").isArray()) {
for (JsonNode jsonNode : node.get("list").get("entries")) {
System.out.println(jsonNode);
}
}
});
Somehow I am not able to map using flatMap. What is missing here?
No since here you are blocking it. Right way would be to do
private Mono<ObjectNode> parseJsonResponse(String folderId) {
return webClient.get()
.uri("/some/rui")
.retrieve().bodyToMono(ObjectNode.class)
.flatMap(node-> {
// do your logic here
})
}
I would say everything what is in mono/flux must stay in mono/flux :) Anytime you call block its blocking your thread.
Related
I am currently trying to implement the Java web service(Rest API) where the endpoint creates the device in the IoTHub and updates the device twin.
There are two methods available in the azure-iot sdk. One is
addDevice(deviceId, authenticationtype)
and another is to
addDeviceAsync(deviceId, authenticationtype)
I just wanted to figure out which one should I use in the web service(as a best practice). I am not very strong in MultiThreading/Concurrency so was wondering to receive people's expertise on this. Any suggestion/Link related to this is much appreciated
Thanks.
The Async version of AddDevice is basically the same. If you use AddDeviceAsync then a thread is created to run the AddDevice call so you are not blocked on it.
Check the code#L269 of RegistryManager doing exactly that: https://github.com/Azure/azure-iot-sdk-java/blob/master/service/iot-service-client/src/main/java/com/microsoft/azure/sdk/iot/service/RegistryManager.java#L269
public CompletableFuture<Device> addDeviceAsync(Device device) throws IOException, IotHubException
{
if (device == null)
{
throw new IllegalArgumentException("device cannot be null");
}
final CompletableFuture<Device> future = new CompletableFuture<>();
executor.submit(() ->
{
try
{
Device responseDevice = addDevice(device);
future.complete(responseDevice);
}
catch (IOException | IotHubException e)
{
future.completeExceptionally(e);
}
});
return future;
}
You can as well build your own async wrapper and call AddDevice() from there.
I am currently working on a personal project - in which I need my Spring application to take queries from an EMQX (MQTT Server) and query its data for corresponding results, and then push the results to a topic with the query UUID.
This is working - after many hours understanding how the Spring Integration framework works. But I think the way in which the handler is using "block" is incorrect - and not in keeping with the manner in which the Integration Flow should operate. Whilst this works I do want to make sure it is being done properly - out of respect for the work - and to avoid future issues.
The code snippet below should be enough to understand what it is that I'm trying to achieve - and where the potential issue lies.
#Bean
fun mqttInFlow() : Publisher<Message<String>> {
return IntegrationFlows.from(inbound())
.handle<String> { payload, headers ->
val emotionalOutput: EmotionalOutput = gson.fromJson(payload, EmotionalOutput::class.java)
emotionalPrintService.populateEmotionalOutput(emotionalOutput).map {
MessageBuilder.withPayload(gson.toJson(it))
.copyHeaders(headers)
.setHeader(MqttHeaders.TOPIC, "query/" + it.query_uuid).build()
}.block()
}
.channel(outgoingChannel())
.toReactivePublisher()
}
EDIT - Thanks for the advice - here is what I understood to be the potential edit for the Kotlin DSL solution - this is now producing an error - complaining that an output-channel or replyChannel was not available - nothing outside of the this function has been changed.
#Bean
fun newMqttInFlow() =
integrationFlow (inbound()) {
wireTap {
handle<String> { payload, headers ->
gson.fromJson<EmotionalOutput>(payload, EmotionalOutput::class.java).let { emotionalOutput ->
emotionalPrintService.populateEmotionalOutput(emotionalOutput).map { populatedEmotionalOutput ->
MessageBuilder.withPayload(gson.toJson(populatedEmotionalOutput))
.copyHeaders(headers)
.setHeader(MqttHeaders.TOPIC, populatedEmotionalOutput.query_uuid)
}
}
}
}
channel("outgoingChannel")
}
Exception is :
exception is org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
Although I have many years experience with Java - this approach is new - so thank you very much for your assistance. It's appreciated. If the whole class would be useful - I can post that.
EDIT
Here is the Configuration file - which might give a better insight into what might be causing this secondary error -
021-03-28 21:59:48.008 ERROR 84492 --- [T Call: divnrin] o.s.integration.handler.LoggingHandler : org.springframework.messaging.MessageHandlingException: error occurred in message handler [bean 'mqttOutbound'; defined in: 'class path resource [io/divnr/appserver/configuration/MQTTConfiguration.class]'; from source: 'org.springframework.core.type.classreading.SimpleMethodMetadata#4a9419d7']; nested exception is java.lang.IllegalArgumentException: This default converter can only handle 'byte[]' or 'String' payloads; consider adding a transformer to your flow definition, or provide a BytesMessageMapper, or subclass this converter for reactor.core.publisher.MonoMapFuseable payloads, failedMessage=GenericMessage [payload=MonoMapFuseable, headers={mqtt_receivedRetained=false, mqtt_id=0, mqtt_duplicate=false, id=c5a75283-c0fe-ebac-4168-dabddd989da9, mqtt_receivedTopic=source/d9e50e8f-67e0-4505-7ca2-4d05b1242207, mqtt_receivedQos=0, timestamp=1616961588004}]
at org.springframework.integration.support.utils.IntegrationUtils.wrapInHandlingExceptionIfNecessary(IntegrationUtils.java:192)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:65)
at
The full class is provided here.
#Configuration
#EnableIntegration
#IntegrationComponentScan
class MQTTConfiguration(val emotionalPrintService: EmotionalPrintService,
val gson: Gson,
val applicationConfiguration: ApplicationConfiguration) {
#Bean
fun mqttServiceFactory() : MqttPahoClientFactory {
return DefaultMqttPahoClientFactory().apply {
connectionOptions = MqttConnectOptions().apply {
serverURIs = arrayOf<String>(applicationConfiguration.mqttServerAddress)
}
}
}
#Bean
fun newMqttInFlow() =
integrationFlow (inbound()) {
handle<String> { payload, headers ->
gson.fromJson<EmotionalOutput>(payload, EmotionalOutput::class.java).let { emotionalOutput ->
emotionalPrintService.populateEmotionalOutput(emotionalOutput).map { populatedEmotionalOutput ->
MessageBuilder.withPayload(gson.toJson(populatedEmotionalOutput))
.copyHeaders(headers)
.setHeader(MqttHeaders.TOPIC, populatedEmotionalOutput.query_uuid).build()
}
}
}
channel(outgoingChannel())
}
#Bean
#ServiceActivator(requiresReply = "false", inputChannel = "outgoingChannel")
fun mqttOutbound(): MessageHandler {
val messageHandler = MqttPahoMessageHandler("divnrout", mqttServiceFactory())
messageHandler.setAsync(true)
return messageHandler
}
#Bean
fun outgoingChannel() : FluxMessageChannel {
return FluxMessageChannel()
}
#Bean
fun inbound(): MessageProducerSupport {
return MqttPahoMessageDrivenChannelAdapter("divnrin", mqttServiceFactory(),
"source/" + applicationConfiguration.sourceUuid).apply {
setConverter(DefaultPahoMessageConverter())
setQos(1)
}
}
}
You indeed don't need that block() in the end of your handle(). You just can return the Mono from that emotionalPrintService.populateEmotionalOutput() and the framework will take for you about the proper subscription and back-pressure handling.
What you would need yet is to make that outgoingChannel() as a FluxMessageChannel.
See more info in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/reactive-streams.html#reactive-streams
Plus consider to move your IntegrationFlow solution to the proper Kotlin DSL: https://docs.spring.io/spring-integration/docs/current/reference/html/kotlin-dsl.html#kotlin-dsl
Also: when it is a FluxMessageChannel in the end of flow, there is no reason to worry about a toReactivePublisher() - the FluxMessageChannel is a Publisher<Message<?>> by itself.
UPDATE
The problem is here:
handle<String>( { payload, headers ->
gson.fromJson<EmotionalOutput>(payload, EmotionalOutput::class.java).let { emotionalOutput ->
emotionalPrintService.populateEmotionalOutput(emotionalOutput).map { populatedEmotionalOutput ->
MessageBuilder.withPayload(gson.toJson(populatedEmotionalOutput))
.copyHeaders(headers)
.setHeader(MqttHeaders.TOPIC, populatedEmotionalOutput.query_uuid).build()
}
}
}) { async(true) }
See that async(true) option. Unfortunately in the current version we don't let it to process reactive reply in the reactive manner by default. You have to say that you'd like to be an async at this end-point. So, your Publisher reply and and FluxMessageChannel as an output is going to do the proper trick.
The Code A is from the artical https://cloud.google.com/speech-to-text/docs/async-recognize
It write with Java, I don't think the following code is a good code, it make the app interrupt.
while (!response.isDone()) {
System.out.println("Waiting for response...");
Thread.sleep(10000);
}
...
I'm a beginner of Kotlin. How can I use Kotlin to write the better code? maybe using coroutines ?
Code A
public static void asyncRecognizeGcs(String gcsUri) throws Exception {
// Instantiates a client with GOOGLE_APPLICATION_CREDENTIALS
try (SpeechClient speech = SpeechClient.create()) {
// Configure remote file request for FLAC
RecognitionConfig config =
RecognitionConfig.newBuilder()
.setEncoding(AudioEncoding.FLAC)
.setLanguageCode("en-US")
.setSampleRateHertz(16000)
.build();
RecognitionAudio audio = RecognitionAudio.newBuilder().setUri(gcsUri).build();
// Use non-blocking call for getting file transcription
OperationFuture<LongRunningRecognizeResponse, LongRunningRecognizeMetadata> response =
speech.longRunningRecognizeAsync(config, audio);
while (!response.isDone()) {
System.out.println("Waiting for response...");
Thread.sleep(10000);
}
List<SpeechRecognitionResult> results = response.get().getResultsList();
for (SpeechRecognitionResult result : results) {
// There can be several alternative transcripts for a given chunk of speech. Just use the
// first (most likely) one here.
SpeechRecognitionAlternative alternative = result.getAlternativesList().get(0);
System.out.printf("Transcription: %s\n", alternative.getTranscript());
}
}
}
You will have to provide some context to understand what you are trying to achieve, but it looks like coroutine is not really necessary here, as longRunningRecognizeAsync is already non-blocking and returns OperationFuture response object. You just need to decide what to do with that response, or just store Future and check it later. There is nothing implicitly wrong with while (!response.isDone()) {}, that's how Java Futures are supposed to work. Also check OperationFuture, if its normal Java Future, it should implement get() method, that will let you wait for result if necessary, without having to do explicit Thread.sleep().
I am sending a value via MyRepository.myConflatedChannel.offer(myvalue).
I then expect to receive it in collect { } or onEach { } blocks in my ViewModel. However, neither function is invoked. It is as if nothing is passed down the ConflatedBroadcastChannel.
Has anybody seen a similar problem?
Make sure you properly work with receiving values.
If you use the ConflatedBroadcastChannel, you can use either OpenSubscription to get a ReceiveChannel or you can represent it as flow (with asFlow).
Note that consume and consumeEach are terminal, they perform an action and then cancel the channel after the execution of the block. See this.
First case:
val receivingChannel = MyRepository.myConflatedChannel.openSubscription()
// then you can consume values using for example a for loop, e.g.:
launch {
for (value in receivingChannel) {
// do something
}
}
Second case:
val receivingFlow = MyRepository.myConflatedChannel.asFlow()
launch {
receivingFlow.collect {
// do something
}
}
I wrote some code in an MVC Framework that looks something like:
class Controller_Test extends Controller
{
public function action_index()
{
$obj = new MyObject();
$errors = array();
try
{
$results = $obj->doSomething();
}
catch(MyObject_Exception $e)
{
$e->getErrors();
}
catch(Exception $e)
{
$errors[] = $e->getMessage();
}
}
My friend argues that the Controller should know nothing about MyObject, and therefore I should not catch MyObject_Exception.
He argues that the code should do something like this instead:
class Controller_Test extends Controller
{
public function action_index()
{
$obj = new MyObject();
$errors = array();
if($obj->doSomething())
{
$results = $obj->getResults();
}
else
{
$errors = $obj->getErrors();
}
}
I definitely understand his approach, but feel as though state management can lead to unintended side effects.
What is the right or preferred approach?
Edit: mistakenly put $obj->getErrors() in MyObject_Exception catch clause instead of $e->getErrors();
The debate about exceptions vs. returned error codes is a long and bloody one.
His argument breaks down in that, by using a getErrors() function, you are learning information about the object. If that is your reason for using a boolean return to indicate success, then you are wrong. In order for the Controller to handle the error properly, it has to know about the object it was touching and what the specific error was. Was it a network error? Memory error? It has to know in some way or another.
I prefer the exception model because it's cleaner and allows me to handle more errors in a more controlled fashion. It also provides a clear cut way for the data relating to an exception to be passed.
However, I disagree with your use of a function like getErrors(). Any data pertaining to the exception that would help me handle it should be included with the exception. I should not have to go hunting into the object again to get information about what went wrong.
Did the network connection timeout? The exception should contain the host/port it tried to connect to, how long it waited, and any data from the lower networking levels.
Let's do this in example (in psuedo c#):
public class NetworkController {
Socket MySocket = null;
public void EstablishConnection() {
try {
this.MySocket = new Socket("1.1.1.1",90);
this.MySocket.Open();
} catch(SocketTimeoutException ex) {
//Attempt a Single Reconnect
}
catch(InvalidHostNameException ex) {
Log("InvalidHostname");
Exit();
}
}
}
Using his method:
public class NetworkController {
Socket MySocket = null;
public Boolean EstablishConnection() {
this.MySocket = new Socket("1.1.1.1",90);
if(this.MySocket.Open()) {
return true;
} else {
switch(this.MySocket.getError()) {
case "timeout":
// Reattempt
break;
case "badhost":
Log("InvalidHostname");
break;
}
}
}
}
Ultimately, you need to know what happened to the object to know how to respond to it, and there is no sense in using some convoluted if statement set or switch-case to determine that. Use the exceptions and love them.
EDIT: I accidentally the last half of a sentence.
In general, I would say that what's important is whether the controller understands the meaning of the exception and can handle it properly. In many cases (if not most), the controller will not know how to properly handle the exception, and so should not catch and handle it.
On the other hand, the controller might reasonably be permitted to understand some specific exception like a "DatabaseUnavailableException", even if it has no idea how or why MyObject used a database. The controller might be permitted to retry the call to MyObject a certain number of times, all without knowing about how MyObject is implemented.
First of all controller is not meant for handling the underlying exceptions thrown by classes.
Even if one occurs controller should halt saying something wrong at underlying error.
This way we make sure that controller does really and only do the job of flow control.
The other classes which give controller some output should be error free unless the error is very much controller specific.